什么是深入响应式原理

● 当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。
● 这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。
● 每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。
● 由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。

一、getter 和 setter

1、{n:0}变成{n:(…)}

一开始是{n:0},传给new Vue之后立马变成{n:(…)}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Vue from "vue";

Vue.config.productionTip = false;

const myData = {
n: 0
};
console.log(myData); //本节课精髓

const vm = new Vue({
data: myData,
template: `
<div>{{n}}<button @click="add">+10</div>
`,
methods: {
add() {
this.n += 10;
}
}
}).$mount("#app");

setTimeout(() => {
myData.n += 10;
console.log(myData); // 本节课精髓
console.log(vm)
}, 3000);

2、get和set

定义的时候设置getter、setter

2.1 需求一:得到姓名

姓名后面的括号能删掉吗?
不能,因为它是函数

1
2
3
4
5
6
7
8
9
10
let obj1 = {
姓: "高",
名: "圆圆",
姓名() {
return this.姓 + this.名;
},
age: 18
};

console.log("需求一:" + obj1.姓名());

2.2 需求二:姓名不要括号也能得出值

总结:getter 就是这样用的。不加括号的函数,仅此而已

1
2
3
4
5
6
7
8
9
10
let obj2 = {
姓: "高",
名: "圆圆",
get 姓名() {
return this.姓 + this.名;
},
age: 18
};

console.log("需求二:" + obj2.姓名);

2.3 需求三:姓名可以被写

总结:setter就是这样用的。用 = xxx 触发 set 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let obj3 = {
姓: "高",
名: "圆圆",
get 姓名() {
return this.姓 + this.名;
},
set 姓名(xxx) {
this.姓 = xxx[0];
this.名 = xxx.substring(1);
},
age: 18
};

obj3.姓名 = "刘诗诗";

console.log(`需求三:姓${obj3.姓}, 名${obj3.名}`);
console.log(obj3);

二、Object.defineProperty

定义完一个对象之后,想再添加新的get、set,只能通过Object.defineProperty
新定义的get、set属性xxx是不存在的,不能return this.xxx/xxx,会造成死循环,可使用局部变量_xxx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
let obj3 = {
姓: "高",
名: "圆圆",
get 姓名() {
return this.姓 + this.名;
},
set 姓名(xxx) {
this.姓 = xxx[0];
this.名 = xxx.substring(1);
},
age: 18
};

var _xxx = 0

Object.defineProperties(obj3, 'xxx',{
get(){
return _xxx
},
set(value){
_xxx = value
}
})

obj3.姓名 = "刘诗诗";

console.log(`需求三:姓${obj3.姓}, 名${obj3.名}`);
console.log(obj3);

三、代理和监听

1、需求一:用Object.defineProperty 定义n

1
2
3
4
5
6
7
let data1 = {}

Object.defineProperty(data1, 'n',{
value:0
})

console.log(`需求一:${data1.n}`)

2、需求二: n 不能小于0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 即data2.n = -1 应该无效,但data2.n = 1 有效

let data2 = {}

data2._n = 0 // _n 用来偷偷存储 n 的值

Object.defineProperty(data2, 'n', {
get(){
return this._n
},
set(value){
if(value < 0){
return
}
this._n = value
}
})

console.log(`需求二:${data2.n}`)
data2.n = -1
console.log(`需求二:${data2.n} 设置为 -1 失败`)
data2.n = 1
console.log(`需求二:${data2.n} 设置为 1 成功`)

// 那如果对方直接使用 data2._n 呢?

3、需求三:使用代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let data3 = proxy({data:{n:0}}) // 括号里是匿名对象,无法访问

function proxy({data}/* 解构赋值*/) {
const obj = {}
// 这里是 'n' 写死了,理论上应该遍历data的所有key,这里做了简化
Object.defineProperty(obj, 'n', {
get(){
return data.n
},
set(value){
if(value<0) {
return
}
data.n = value
}
})
return obj // obj 就是代理
}

// data3 就是 obj
console.log(`需求三:${data3.n}`)
data3.n = -1
console.log(`需求三:${data3.n}, 设置为 -1 失败`)
data3.n = -1
console.log(`需求三:${data3.n}, 设置为 1 失败`)

4、需求四

1
2
3
4
5
6
7
8
9
let myData = {n:0}
let data4 = proxy({data:myData}) // 括号里是匿名对象,无法访问

// data3 就是 obj
console.log(`杠精:${data4.n}`)
myData.n = -1
console.log(`杠精:${data4.n}, 设置为 -1 失败了吗!?`)

// 我现在改 myData,是不是还能改?!

5、需求五:用户擅自改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
let myData5 = {n:0}
let data5 = proxy2({ data:myData5}) // 括号里是匿名对象,无法访问

function proxy2({data}/* 解构赋值 */){
// 这里的'n'写死了,天蝎座上应该遍历data的所有key,这里做了简化
// 因为我怕你们看不懂
let value = data.n
delete data.n // 这句话可以不写
Object.defineProperty(data, 'n',{
get(){
return value
},
set(newValue){
if(newValue<0){
return
}
value = newValue
}
})
// 就加了上面几句,这几句话会监听data

const obj = {}
Object.defineProperty(obj, 'n', {
get(){
return data.n
},
set(value){
if(value<0){
return
}
data.n = value
}
})

return obj // obj 就是代理
}

// data3 就是obj
console.log(`需求五:${data5.n}`)
data5.n = -1
console.log(`需求五:${data5.n}, 设置为 -1 失败`)
data5.n = -1
console.log(`需求五:${data5.n}, 设置为 1 失败`)

// 这代理看着眼熟吗?
// let data5 = proxy2({data:myData5})
// let vm = new Vue({data:{n:0}})

1、computed是计算属性的意思

1.1 computed是用来计算出一个值的,这个值我们调用是不需要加括号

1.2 根据依赖会自动缓存

1.3 不支持异步代码

2、watch是监听的意思

2.1 watch支持异步代码

2.2 有两个选项

2.2.1 immediate 第一次渲染的时候要执行函数

2.2.2 deep 是否要监听对象里面的属性变化。如果某个属性变化了就执行函数。


一、泛型

泛型用一个东西来表示广泛类型

1、泛型定义

泛型定义

2、interface

interface

3、return数组

return数组

4、加尖括号

加尖括号

5、泛型类

泛型类

二、泛型约束

1、泛型约束

泛型约束


功能:父组件传数据给子组件,子组件改prop值并通知父组件

1、无.sync实现方法

子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div class="child">
{{money}}
<button @click="$emit('update:money', money - 100)">
<span>花钱</span>
</button>
</div>
</template>

<script>
export default {
props:["money"]
};
</script>

父组件

1
2
3
4
5
6
7
<template>
<div id="app">
App.vue 我现在有 {{total}}
<hr>
<Child :money="total" v-on:update:money ="total = $event" />
</div>
<template>

2、有.sync修饰符实现方法

父组件

1
2
3
4
5
6
7
<template>
<div id="app">
App.vue 我现在有 {{total}}
<hr>
<Child :money.sync="total" />
</div>
</template>

子组件

不变

3、修饰符sync小结

3.1 功能

当一个子组件改变了一个 prop 的值时,这个变化也会同步到父组件中所绑定。

3.2 缩写

:money.sync="total" 等价于 :money="total" v-on:update:money ="total = $event"

3.3 是一个语法糖


1、安装依赖插件vite-plugin-svg-icons

1
yarn add vite-plugin-svg-icons -D 

1
npm i vite-plugin-svg-icons -D

2、安装glob

不安装会提示Error: Cannot find module 'fast-glob'

1
yarn add fast-glob -D

1
npm install fast-glob -D

3、配置vite.config.ts

3.1 iconDirs所有的 svg的文件都存放在该文件夹下

3.2 symbolId指定use标签中href格式

导出名为createSvgIconsPlugin并加大括号,不然会提示TypeError: (0 , import_vite_plugin_svg_icons.default) is not a function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { defineConfig } from 'vite'
import { resolve } from 'path'
import {createSvgIconsPlugin} from 'vite-plugin-svg-icons';

export default defineConfig({
plugins: [
createSvgIconsPlugin({
// 指定要缓存的图标文件夹
iconDirs: [path.resolve(process.cwd(), 'src/svg')],
// 执行icon name的格式
symbolId: 'icon-[name]',
})
]
});

3、在main.ts注册脚本

1
import 'virtual:svg-icons-register'; // 引入svg icon注册脚本

4、通过svg实现Icon组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<template>
<svg :class="svgClass" aria-hidden="true">
<use :xlink:href="iconName" :fill="color" />
</svg>
</template>

<script>
import { computed, defineComponent } from 'vue'
export default defineComponent({
props: {
name: {
type: String,
default: ''
},
color: {
type: String,
default: ''
},
},
setup(props) {
return {
iconName: computed(() => `#icon-${props.name}`),
svgClass: computed(() => {
if (props.name) {
return `gulu-icon ${props.name}`
}
return 'gulu-icon'
})
}
}
})
</script>

<style lang="scss" scoped>
.gulu-icon {
width: 3em;
height: 3em;
position: relative;
fill: currentColor;
vertical-align: -2px;
}
</style>

5、引用自定义Icon组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<Icon name = "alipay" />
<Icon name = "qq" />
<Icon name = "wechat" />
</template>

<script lang="ts">
import {
Icon,
} from './lib/index'

export default {
components: {
Icon
}
}
</script>

【问题描述】

点击某一具体标签报404,但是本地没有问题

【分析】

  • 发现这些报404的标签或分类名都是修改过的,原先的标签开头是大写名称
  • 将网址后面的标签名首字母改成大写,就会显示标签下的文章

【原因】

由于git中设置了忽略大小写

【解决办法】

设置 git 不忽略大小写

1、进入博客文件夹,编辑 git 目录下的配置文件

1
2
3
cd blog
vim .deploy_git/.git/config
i // 这时左下角变成--INSERT--,可修改模式

2、修改内容

将光标移到ignorecase=true这一行,将ignorecase值改为false

1
2
# ignorecase = true
ignorecase = false

3、保存修改内容

按esc,输入:号,左下角就变成:号,在左下角输入wq回车

4、查看是否修改成功

1
cat .deploy_git/.git/config 

5、重写清空部署项目

5.1 如果还没有解决,清空部署到 github 上的文件,重新发布:

1
2
3
4
cd .deploy_git
git rm -rf *
git commit -m 'clean all file'
git push

5.2 进入博客文件夹,用 hexo 重新生成部署

1
2
hexo clean
hexo d -g

方法1:将图片放置source/images中上传图片

1、在source目录下新建文件images

2、将图片复制到images文件夹中

Hexo默认从根目录下的/source/images/文件夹寻找图片

3、引入图片

注:路径以images开头

1
![pic](images/pic.png)

方法2:在同名文件夹中上传图片

1、让每篇新建的文章生成一个与文章同名的文件夹

打开本目录下的配置文件_config.yml,将post_asset_folder这个选项设置为true

1
post_asset_floder: true
  • 此时运行hexo new xxx,在/source/_posts文件夹下,除了会生成xxx.md文件,还会生成一个同名文件夹
  • 此时上传代码到github时,会将同名文件夹一并上传(设置false时不会上传)

2、让Hexo改为从与文章同名的资源文件夹中寻找图片

2.1 安装插件hexo-asset-image

1
npm install hexo-asset-image --save

2.2 插件hexo-asset-image说明

  • hexo-asset-image是一个Hexo的插件,它针对的是那些为每篇博文独立设立资源文件夹(通过post_asset_folder设为true)的用户。

  • 如果你在Hexo文章中使用![pic](pic.jpg)这样的标记来引用图片,那么Hexo默认从根目录下的/source/images/文件夹寻找图片,而非从当前文章的资源文件夹中寻找。

  • 如果你安装并启用了hexo-asset-image插件,那么Hexo会改为从与文章同名的资源文件夹中寻找图片。

  • 当你安装并启用hexo-asset-image插件后,可以直接把图片放到文章的资源文件夹中,然后在文章中使用相对路径引用图片,比如![pic](pic.jpg),Hexo会自动从文章的资源文件夹中寻找pic.jpg

3、在文章中添加图片

3.1 创建新文章

运行hexo new xxx生成的博文,此时在/source/_posts文件夹下,除了会生成xxx.md文件,还会生成一个同名文件夹

3.2 添加图片

xxx.md中引入本地图片时,先把图片复制到同名文件夹下,然后在需要引用图片的地方按照markdown的格式输入

1
![图片标题](图片名.jpg)

注:

  • 文件夹xxx与xxx.md文件需要在同一层级的目录下
  • 在xxx.md文件中引入图片时,直接输入图片名,不要输入具体路径

错误示范

1
![图片标题](./xxx/图片名.jpg)

注册必力账号 网页

点右上角头像,再点登陆,跳转页面后,点来必力注册

登陆界面

第一行填注册邮箱,第二行用户名,第三行密码

注册界面

安装

在注册成功后,跳转到安装界面,选择City版本进行安装。 点击现在安装。

安装

输入自己的网址(带https://),网站名(随便填),和网站类型(选择个人网站)即可。

网址

获取data-uid

申请成功后,进入到代码管理界面。找到以下内容。

1
<div id="lv-container" data-id="city" data-uid="xxxxxxx">

复制data-uid里面的那一串字段,配置要用。

uid

配置文件

打开你当前主题目录下的配置文件(路径:themes/next/_config.yml),搜索livere,找到如下内容,把之前data-uid里面的字段复制到livere-uid:后面(注意字段和冒号中间要有空格,#号去掉)。

1
2
3
# LiveRe comments system
# You can get your uid from https://livere.com/insight/myCode (General web site)
livere_uid: #<your_uid>

开启livere评论系统

在你当前主题目录下的配置文件中,搜索comments,找到如下内容,在active:后面打上livere。

1
2
3
4
5
6
7
8
9
10
comments:
# Available values: tabs | buttons
style: tabs
# Choose a comment system to be displayed by default.
# Available values: changyan | disqus | disqusjs | gitalk | livere | valine
active: livere
# Setting `true` means remembering the comment system selected by the visitor.
storage: true
# Lazyload all comment systems.
lazyload: false

最后效果

运行代码

1
2
hexo d -g
hexo s

效果如图

评论区


属性赋值,层叠(Cascading)和继承

1、指定值,计算值和实际值

一旦用户代理已经解析了文档并构造好了文档树,它就必须给树中的每个元素上适用于目标媒体类型的每个属性赋值

属性的最终值是4步计算的结果:先通过指定来确定值(“指定值(specified value)”),接着处理得到一个用于继承的值(“计算值(computed value)”),然后如果有必要的话转化为一个绝对值(“应用值(used value)”),最后根据本地环境限制进行转换(“实际值(actual value)”)

1.1 指定值

用户代理必须先根据下列机制(按优先顺序)给每个属性赋值一个指定值:

1.1.1 如果层叠产生了一个值,就使用它

1.1.2 否则,如果属性是继承的并且该元素不是文档树的根元素,使用其父元素的计算值

1.1.3 否则使用属性的初始值,每个属性的初始值都在属性定义中指出了

1.2 计算值

指定值通过层叠被处理为计算值,例如,URI被转换成绝对的,’em’和’ex’单位被计算为像素或者绝对长度。计算一个值并不需要用户代理渲染文档

UA无法处理为绝对URI的话,该URI的计算值就是指定值

一个属性的计算值由属性定义中Computed Value行决定。当指定值为’inherit’时,计算值的定义见继承小节

即使属性不适用(于当前元素),其计算值也存在,定义在’Applies To’行。然而,有些属性可能根据属性是否适用于该元素来定义元素属性的计算值

1.3 应用值

处理计算值时,尽可能不要格式化文档。然而,有些值只能在文档布局完成时确定。例如,如果一个元素的宽度是其包含块的特定百分比,在包含块的宽度确定之前无法确定这个宽度。应用值是把计算值剩余的依赖(值)都处理成绝对值后的(计算)结果

1.4 实际值

原则上,应用值应该用于渲染,但用户代理可能无法在给定的环境中利用该值。例如,用户代理或许只能用整数像素宽度渲染边框,因此不得不对宽度的计算值做近似处理,或者用户代理可能被迫只能用黑白色调而不是全彩色。实际值是经过近似处理后的应用值

2、继承

有些值是从文档树中某个元素的子级继承来的,就像上面描述的一样。每个属性都定义了它是否可继承

假设H1元素里有个强调元素(EM):
<H1>The headline <EM>is</EM> important!</H1>

如果没有给EM元素赋值颜色,强调的”is”将从其父元素继承颜色,所以如果H1是蓝色的,EM元素同样也将是蓝色的

发生继承时,元素继承计算值。来自父元素的计算值同时作为指定值和其子级的计算值

例如,给出如下样式表:

1
2
body { font-size: 10pt }
h1 { font-size: 130% }

和文档片段:

1
2
3
<BODY>
<H1>A <EM>large</EM> heading</H1>
</BODY>

H1元素的’font-size’属性将获得计算值’13pt’(130%乘以父元素的值10pt)。因为’font-size’的计算值是继承的,EM元素也将获得计算值’13pt’。如果用户代理没有可用的13pt字体,H1和EM的’font-size’的实际值可能是,例如,’12pt’

注意,继承遵循文档树并且不会被匿名框截断

2.1 ‘inherit’值

每个属性也可能有一个’inherit’层叠值,表示对于一个给定的元素,属性采用与其父级属性相同的指定值。’inherit’值可以用来实现值的继承,并且它也可以用在那些一般不是继承的属性上

如果’inherit’值设置在根元素上,该属性会被赋值为其初始值

下例中,’color’和’background’属性被设置在BODY元素上。对于其它所有元素,’color’值都将被继承并且背景将是透明的。如果这些规则是用户样式表的一部分,整个文档都将是白色背景上的黑色文本

1
2
3
4
5
6
7
8
9
body {
color: black !important;
background: white !important;
}

* {
color: inherit !important;
background: transparent !important;
}

3、@import规则

‘@import’规则允许用户从其它样式表引入样式规则。CSS 2.1中,任何@import规则必须位于所有其它规则(除了@charset,如果有的话)之前。关于用户代理什么情况下必须忽略@import规则,见解析章节。‘@import’关键字后面必须跟着想要引入的样式表的URI。字符串也可以接受,也可以表示成周围有url(…)的形式

下面这几行在意义上是等价的,都描述了‘@import’语法(一个有”url()”,另一个是裸字符串):

1
2
@import "mystyle.css";
@import url("mystyle.css");

为了让用户代理能够避免为不支持的媒体类型检索资源,编写者可以指定带媒体依赖(media-dependent)的@import规则。这种条件引入在URI后面指定了逗号分隔的媒体类型

下列规则描述了@import规则怎样变成带媒体依赖的:

1
2
@import url("fineprint.css") print;
@import url("bluish.css") projection, tv;

缺少媒体类型时,引入是无条件的,与把媒体指定为’all’效果一样。引入只在目标媒体匹配媒体列表时才生效

如果媒体列表中有一项是目标媒体或者’all’时,目标媒体匹配媒体列表

注意,媒体查询[MEDIAQ]扩展了媒体列表的语法和匹配的定义

引入相同样式表或者在多处链接同一文档时,用户代理必须处理(或者表现得像那样)每一个链接,就像链接都指向一个单独的样式表一样

4、层叠(cascade)

样式表可能有3种不同来源:编写者,用户和用户代理

  • 编写者:编写者根据文档语言约定给源文档指定样式表。例如,HTML中,样式表可以包含在文档中或者从外部链接
  • 用户:用户可能会给某个特定文档指定样式信息。例如,用户可以指定一个含有样式表的文件,或者用户代理可能会提供一个用来生成用户样式表(或者表现得像这样做了一样)的界面
  • 用户代理: (与CSS规范)一致的用户代理必须应用一份默认样式表(或者表现得像它做了一样)。用户代理的默认样式表应该以满足文档语言一般表现预期的方式来呈现文档语言元素(例如,对于可视化浏览器,HTML中EM元素用斜体来表示)。关于HTML元素的推荐默认样式表,见一份简单的HTML样式表
    注意,用户可能会修改系统设置(例如,系统配色),这会影响默认样式表。然而,有些用户代理实现让默认样式表中的值不可改变

这3种样式表将在一定范围内重叠,并且它们按照层叠互相影响

CSS层叠给每个样式规则赋予了权重。应用几条规则时,权重最大的优先

默认情况下,编写者样式表中的规则比用户样式表中的规则权重高。然而,对于”!important”规则,优先级却是相反的。所有用户和编写者规则都比UA默认样式表中的规则权重高

4.1 层叠顺序

为了找出一个元素/属性组合的值,用户代理必须按照下列(步骤)排序:

  1. 找出目标媒体类型下,所有适用于该元素和目标属性的声明。如果关联的选择器匹配该元素,并且目标媒体类型与含有该声明的@media规则列出的所有媒体类型,以及到达样式表的路径上所有链接匹配,则声明适用(Declarations apply if the associated selector matches the element in question and the target medium matches the media list on all @media rules containing the declaration and on all links on the path through which the style sheet was reached.)
  2. 根据重要性(常规或重要)和来源(编写者,用户或用户代理)排序,升序优先级为:
    • 用户代理声明
    • 用户常规声明
    • 编写者常规声明
    • 编写者重要声明
    • 用户重要声明

  3. 相同重要性和来源的规则根据选择器的特殊性(specificity)排序:更特殊的选择器将重写一般的。伪元素和伪类被分别算作常规元素和类
  4. 最后,根据指定顺序排序:如果两个声明的权重,来源和特殊性都相同,后指定的生效。引入的样式表(译注:这里应该是指‘@import’,而不是广义的通过各种方式引入样式表)中的声明被认为在样式表自身的所有声明之前

除了给个别声明设置”!important”外,比起读者样式表,这种策略给了编写者样式表更高的权重。用户代理必须给予用户去除特定编写者样式表影响的能力,例如,通过一个下拉菜单。与UAAG 1.0 checkpoint 4.14一致的(用户代理)满足该条件[UAAG10]

4.2 !important规则

CSS尝试在编写者和用户样式表之间建立平衡。默认情况下,编写者样式表中的规则会重写那些位于用户样式表中的(见层叠规则3)

然而,为了平衡,”!important”声明(声明后面跟着分隔符token “!”和关键字”important”)比常规声明优先。编写者和用户样式表都可能含有”!important”声明,并且用户”!important”规则会重写编写者”!important”规则。通过给予用户表现上的特殊需求(大字体,颜色组合等等)控制,该CSS特性提高了文档的可访问性

声明一个简写属性(例如,’background’)为”!important”等价于声明其所有子属性为”!important”

下例中用户样式表的第一条规则含有一个”!important”声明,重写了编写者样式表中的相关声明。第二条声明也将生效,因为被标记为”!important”了。然而,用户样式表中的第三条规则不是”!important”,因此被编写者样式表(恰好在一条简写属性中设置了)中的第二条规则重写了。而且,第三条编写者规则将被第二条编写者规则重写,因为第二条规则是”!important”。这说明”!important”声明在编写者样式表中也有效

1
2
3
4
5
6
7
8
9
/* From the user's style sheet */
p { text-indent: 1em ! important }
p { font-style: italic ! important }
p { font-size: 18pt }

/* From the author's style sheet */
p { text-indent: 1.5em !important }
p { font: normal 12pt sans-serif !important }
p { font-size: 24pt }

4.3 计算选择器的特殊性(specificity)

一个选择器的特殊性是根据下列(规则)计算的:

  • 如果声明来自一个’style’属性而不是一条选择器样式规则,算1,否则就是0 (= a)(HTMl中,一个元素的”style”属性值是样式表规则,这些属性没有选择器,所以a=1,b=0,c=0,d=0)
  • 计算选择器中ID属性的数量 (= b)
  • 计算选择器中其它属性和伪类的数量 (= c)
  • 计算选择器中元素名和伪元素的数量 (= d)

特殊性只根据选择器的形式来定。特殊的,一个”[id=p33]”形式的选择器被算作一个属性选择器(a=0, b=0, c=1, d=0),即使id属性在源文档的DTD中被定义为”ID”

4个数连起来a-b-c-d(在一个基数很大的数字系统中(in a number system with a large base))表示特殊性

一些示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *             {}  /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
li {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
li:first-line {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul li {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul ol+li {} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
h1 + *[rel=up]{} /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
ul ol li.red {} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
li.red.level {} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
#x34y {} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
style="" /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */

<HEAD>
<STYLE type="text/css">
#x97z { color: red }
</STYLE>
</HEAD>
<BODY>
<P ID=x97z style="color: green">
</BODY>

上面的示例中,P元素的颜色将是绿色,”style”属性中的声明将重写STYLE元素中的,因为层叠规则3,它的特殊性更高

4.4 非CSS表现型提示(presentational hints)的样式优先级

UA可能选择尊重HTML源文档中的表现型属性。如果这样的话,这些属性会被翻译成相应的特殊性为0的CSS规则,并且就当它们像是被插在编写者样式表开头的一样。因此,它们可以被后续样式表符规则重写。在过渡阶段,这种策略让表现型属性(stylistic attributes)更容易与样式表共存

对于HTML,下面列表之外的任何属性都应该被认为是表现性的:abbr,accept-charset,accept,accesskey, action,alt,archive,axis,charset,checked,cite,class,classid, code,codebase,codetype,colspan,coords,data,datetime,declare, defer,dir,disabled,enctype,for,headers,href,hreflang, http-equiv,id,ismap,label,lang,language,longdesc,maxlength, media,method,multiple,name,nohref,object,onblur,onchange, onclick,ondblclick,onfocus,onkeydown,onkeypress,onkeyup,onload, onload,onmousedown,onmousemove,onmouseout,onmouseover,onmouseup, onreset,onselect,onsubmit,onunload,onunload,profile,prompt, readonly,rel,rev,rowspan,scheme,scope,selected,shape,span, src,standby,start,style,summary,title,type(LI,OL和UL元素上的除外),usemap,value,valuetype,version

对于其它语言,所有基于文档语言的样式必须被翻译成相应的CSS,并且要么算进用户代理级的层叠中,要么与HTML表现型提示一起被当作特殊性为0的编写者级规则放在编写者样式表开头

下列用户样式表将会重写所有文档中’b’元素的字重,以及XML文档中具有color属性的font’元素的颜色。它将不会影响HTML文档中任何具有color属性的’font’元素的颜色(译注:实际上,在HTML里这个样式对有color属性的font元素也是有效的。但一致性章节有声明“示例和注意事项是非规范的”,这里是示例):

1
2
b { font-weight: normal; }
font[color] { color: orange; }

然而,下面的(样式表)将会重写所有文档中font元素的颜色:

font[color] { color: orange ! important; }

5、小结

5.1 选择器越具体,其优先级越高

5.2 相同优先级,出现在后面的,覆盖前面的

5.3 属性后面加 !important 的优先级最高,但是要少用


第一种盒模型是 content-box,即 width 指定的是 content 区域宽度,而不是实际宽度,公式为

实际宽度 = width + padding + border

第二种盒模型是 border-box,即 width 指定的是左右边框外侧的距离,公式为

实际宽度 = width

相同点是都是用来指定宽度的,不同点是 border-box 更好用。


0%