导航
[封装01-设计模式] 设计原则 和 工厂模式(简单抽象方法) 适配器模式 装饰器模式
[封装02-设计模式] 命令模式 享元模式 组合模式 代理模式
[封装03-设计模式] Decorator 装饰器模式在前端的应用
[封装04-设计模式] Publish Subscribe 发布订阅模式在前端的应用
[封装05-ElementUI源码01] Row Col Container Header Aside Main Footer
[React 从零实践01-后台] 代码分割
[React 从零实践02-后台] 权限控制
[React 从零实践03-后台] 自定义hooks
[React 从零实践04-后台] docker-compose 部署react+egg+nginx+mysql
[React 从零实践05-后台] Gitlab-CI使用Docker自动化部署
[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] koa
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染
[源码-vue02] computed 响应式 - 初始化,访问,更新过程
[源码-vue03] watch 侦听属性 - 初始化和更新
[源码-vue04] Vue.set 和 vm.$set
[源码-vue05] Vue.extend
[源码-vue06] Vue.nextTick 和 vm.$nextTick
[源码-react01] ReactDOM.render01
[源码-react02] 手写hook调度-useState实现
[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI
[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数
[深入21] 数据结构和算法 - 二分查找和排序
[深入22] js和v8垃圾回收机制
[深入23] JS设计模式 - 代理,策略,单例
[深入24] Fiber
[深入25] Typescript
[深入26] Drag
[前端学java01-SpringBoot实战] 环境配置和HelloWorld服务
[前端学java02-SpringBoot实战] mybatis + mysql 实现歌曲增删改查
[前端学java03-SpringBoot实战] lombok,日志,部署
[前端学java04-SpringBoot实战] 静态资源 + 拦截器 + 前后端文件上传
[前端学java05-SpringBoot实战] 常用注解 + redis实现统计功能
[前端学java06-SpringBoot实战] 注入 + Swagger2 3.0 + 单元测试JUnit5
[前端学java07-SpringBoot实战] IOC扫描器 + 事务 + Jackson
[前端学java08-SpringBoot实战总结1-7] 阶段性总结
[前端学java09-SpringBoot实战] 多模块配置 + Mybatis-plus + 单多模块打包部署
[前端学java10-SpringBoot实战] bean赋值转换 + 参数校验 + 全局异常处理
[前端学java11-SpringSecurity] 配置 + 内存 + 数据库 = 三种方式实现RBAC
[前端学java12-SpringSecurity] JWT
[前端学java13-SpringCloud] Eureka + RestTemplate + Zuul + Ribbon
复习笔记-01
复习笔记-02
复习笔记-03
复习笔记-04
前置知识
(1) 一些单词
gutter 阴沟 槽
imitate 模仿
chalk 粉笔 // theme-chalk
primitive 原始的 落后的
incompatible 不兼容
复制代码
(2) render 和 createElement
- render函数签名:
(createElement: () => VNode) => VNode
- createElement 源码链接
createElement()
---
1. 返回值
- createElement() 返回一个 VNode
2. 参数
- 第一个参数
- {String | Object | Function}
- HTML标签名、组件选项对象(其实就是一个组件),或者 resolve 了上述任何一种的一个 async 函数 -- 必填
- 第二个参数
- {Object}
- 一个与模板中 attribute 对应的数据对象 -- 可选
- 第二个参数其实就是 ( 数据对象 ),官网链接
- https://cn.vuejs.org/v2/guide/render-function.html#createElement-%E5%8F%82%E6%95%B0
- 从官网中我们需要学习到
- 1. render和template相比的好处
- 2. 第二个参数-即数据对象的属性有哪些,比如 class,style,attrs,props,domProps,on,nativeOn,directives,scopedSlots,slot,key,ref,refInFor
- 3. render()方法中的一些约束
- 第三个参数
- {String | Array}
- children
- 子级虚拟节点 (VNodes) 组成的数组,由 `crateElement()` 构建而成的children -- 可选
- 也可以使用字符串来生成“文本虚拟节点” -- 可选
- 参数注意点
- 第二个和点三个参数是可选的
- 当只有两个参数时,源码中会做 ( 参数重载 ),第二个参数会被当作第三个参数来处理
3. createElement() 的一些使用方式
- 1. createElement('div', 'text1text2') 中的第二个参数会作为 这里的第三个参数(源码中的第四个参数) children来处理
- 2. createElement('div', [createElement()]) 中的第二个参数会作为 这里第三个参数(源码中的第四个参数) children来处理
- 3. createElement('div', {class, style, ...}, 'text1')
- 4. createElement({data, props, ...}, 'text')
- 5. ...
4. 案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="../dist/vue.js"></script>
</head>
<body>
<div id="app"></div>
<script>
new Vue({
el: "#app",
render(createElement) {
return createElement(
"div",
{
attrs: {
// 注意:class 和 style 不在 attrs 中
id: "wrap-id",
"data-href": "custom-attribute",
// class: 'is-right' 不生效
},
style: {
width: "700px",
height: "200px",
border: "1px solid red",
},
class: {
wrapper: true,
container: true,
root: false,
},
on: {
click() {
console.log("点击父元素会触发的事件");
},
},
},
[
createElement({
data() {
return {
name: "woow_wu7",
};
},
template: `
<div>
<p>{{name}}</p>
<button @click.stop="changeName">子组件的按钮</button>
</div>
`,
methods: {
changeName() {
this.name = this.name + +new Date();
console.log(
`这里我们使用了事件修饰符 stop , 相当于event.stopPropagation,不然父组件上绑定的click事件也会触发`
);
},
},
}),
createElement("p", "p标签"),
]
);
},
});
</script>
</body>
</html>
复制代码
(3) slots 和 scopedSlots 的区别
- vm.$slots:用来访问被插槽分发的内容,范围小,不包含作用域插槽
- vm.$scopeSlots:用来访问作用域插槽,范围大,包含了所有插槽
- 区别
vm.$scopeSlots 范围大,包含了所有插槽
vm.$slots 范围小,不包含作用域插槽
具名插槽 和 作用域插槽
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="../dist/vue.js"></script>
</head>
<body>
<div id="app">
<Child>
<template v-slot:default> <p>这是一个具名default插槽</p> </template>
<template v-slot:content="contentFromChild">
<div style="border: 1px solid red">
<p>这是一个具名content插槽</p>
<p>!!插槽中的内容的作用域绑定在父级!!--> {{fatherUser}}</p>
<p>!!但是可以使用作用域插槽,获取子作用域中的数据,通过v-slot:content="子作用中的数据组装成props来获取子作用域props" --> {{contentFromChild.childUser}}
</p>
</div>
</template>
</Child>
</div>
<script>
Vue.component("Child", {
data() {
return {
childUser: "子作用域中的数据childUser",
};
},
mounted() {
console.log(`this.$slots -> 范围较小,不包含作用域插槽`, this.$slots)
console.log(`this.$scopedSlots -> 范围更大,包含了所有的slot`, this.$scopedSlots)
},
template: `
<div>
<p>Child组件</p>
<slot name="default"/>
<slot name="content" :childUser="childUser"/>
</div>
`,
});
new Vue({
el: "#app",
data() {
return {
fatherUser: "父作用域中的数据fatherUser",
};
},
});
</script>
</body>
</html>
复制代码
额外补充
---
vm.$parent 父元素
vm.$children 当前实例的直接子组件,需要注意 `$children` 并不保证顺序,也不是响应式的。**
vm.$root 当前组件树的根Vue实例
vm.$attrs 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (`class` 和 `style` 除外)
vm.$listeners 包含了父作用域中的 (不含 `.native` 修饰器的) `v-on` 事件监听器
vm.$slots 用来访问被插槽分发的内容,范围小,不包含作用域插槽
vm.$scopeSlots 用来访问作用域插槽,范围大,包含了所有插槽
vm.$data
vm.$props
vm.$el
vm.$options
vm.$isServer
复制代码
(4) Vue-Plugin 和 Vue.use
- 插件:通常来为vue添加 全局功能
- Vue.use(plugin, options)
- 作用:注册插件
- 参数:
- plugin
{Object | Function}
- options 表示可选对象,可选
- plugin
Vue.use() 需要在 new Vue() 之前完成
Vue.use()会自动阻止多次注册相同的插件,即使多次调用 Vue.use() 也只会注册一次该插件
- 开发插件
- 插件是对象:每个插件都有一个
install
方法,该方法参数是 Vue,和 配置对象 - 插件是函数:自身当作install函数
- 插件是对象:每个插件都有一个
1. 编写Vue的插件
---
MyPlugin.install = function (Vue, options) {
Vue.myGlobalMethod = function () {} // 添加 全局静态方法 或 property
Vue.prototype.$myMethod = function (methodOptions) { } // 添加实例方法,实例方法一般会用$符,表示vue中的保留字段,供vue内部使用,所以一般规定在Vue.prototype上挂载的属性和方法都要以 $ 开头
Vue.directive('my-directive', {}) // 添加全局资源
Vue.mixin({ // 注入组件选项
created: function(){...表示在所有组件中都混入created钩子函数中的代码}
})
}
复制代码
2. Vue.use() 注册并使用Vue插件
---
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
复制代码
3. Vue.use() 源码
- Vue.use()作用很简单
- 1. 先判断插件是对象还是函数,对象执行install,函数把自身当作install
- 2.注册过该插件就不在注册,返回Vue
- 3.未注册,执行plugin,并维护一个插件数组用来判断是否已经注册过插件,添加进数组插件,返回Vue
---
export function initUse(Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
// 每个 Vue 的 plugin 是对象时都具有一个install方法,是函数时本身会被当作install方法
const installedPlugins = this._installedPlugins || (this._installedPlugins = []);
if (installedPlugins.indexOf(plugin) > -1) {
// 如果已经注册过该插件,返回this
// this表示大Vue,返回this,表示不再往下执行,同时可以实现链式调用,Vue.use().use()
return this;
}
// additional parameters
// - Vue.use(plugin, additionalParameters) 附加的可选的第二个配置项对象参数
const args = toArray(arguments, 1);
// args
// - 注意这里的 arguments 是 Vue.use()方法中的 arguments
// - 这里表示:获取 Vue.use() 的第二个参数 options 对象
args.unshift(this);
// 将大 Vue 添加进参数数组最前面,这样调用plugin的install方法的第一个参数就变成了Vue
if (typeof plugin.install === "function") {
// 插件是对象,install方法存在并且是function,调用
// 注意:vue的plugin中的install方法的第一个参数:大Vue,就是args数组的第一个成员就是大Vue, args.unshift(this);
plugin.install.apply(plugin, args);
} else if (typeof plugin === "function") {
// 插件本身是函数,调用
plugin.apply(null, args);
}
installedPlugins.push(plugin); // 为注册过该插件,调用插件后,把该插件添加进插件数组,用于之后判断是否注册过该插件
return this; // 同样的,插件 注册过 和 未注册过 都在最后返回 Vue,实现Vue.use() 的链式调用
};
}
复制代码
(5) BEM 命名规范
- BEM 是 (
block块
) (element元素
) (modifier修饰符
) 的简写 - BEM是一种以组件为基础的开发模式、规范!
1. 语法
block: {} ---------------------- 块
block__element: {} ------------- 元素
block__element--modifier: {} --- 修饰符,表示 ( 块中元素 ) 的 ( 状态,属性,外观,修饰 ) 等附加的样式
2.
- 问题1
- 问题:为什么要使用两个__和两个--,而不是一个?
- 回答:因为是为了和 ( 块 ) 做区分,遇到 ( - ) 的就是块
- 问题2
- 问题:什么时候应该建立一个block或element
- 回答:
- 1、如果一个板块将来需要复用,并且不依赖其它任何组件,那么这个时候就应该建立一个block
- 2、如果一个板块不能离开一个实体而单独使用,那么这个时候就应该建立一个element
3. 优点
- BEM可以在dom-tree结构改变时候,不用修改css就能使用
- dom-tree修改前
<div class="block">
<div class="block__elem1">
<div class="block__elem2"></div> ( 或者使用 <div class="block__elem1-elem2"></div> 的写法都可以 )
</div>
</div>
- dom-tree修改后
- dom-tree的结构从嵌套变成了同级时,我们不需要修改css的命名
<div class="block">
<div class="block__elem1"></div>
<div class="block__elem2"></div>
</div>
3. block
- 含义:可以理解为 ( 一个组件就是一个block ),它是一个 ( 以后你可以复用 ) 的单元
- 使用:可以直接使用,嵌套使用
4. element
- 含义:element一定是block的一部分,并且 ( 不能离开block单独使用 )
- 使用:可以直接使用,嵌套使用
- 注意点:
- 1
- element对应的是block的一部分,而不是另一个element的一部分
- 正确:block__element
- 错误:block__element1__element2,因为element2作为了element的部分,不正确
- 改正:
- block__element1 和 block__element2 的方式,即使element2是element1的子元素
- block__element1-element2 这种写法也可以
- 2
- element对于block来说是可选的,并不是所有的block都要有element
- 3
- element是block的一部分,不能脱离block单独使用
5. modifier
- 注意点
- 1、Modifier不能离开Block和Element单独使用
复制代码
(6) Sass
1
@mixin 和 @include 和 @content
- @mixin aa -----> 定义一个混合器aa
- @include aa ---> 使用混合器aa
- @content; ------> 将include aa 中定义的样式引入到@mixin中
---
<style scoped lang="scss">
@mixin test-mixin($color: red) { // ---- @mixin
// 1
// $color: red
// 这里冒号后面是默认值,在没有传入参数时,默认值生效
div {
background: $color;
@content; // ----------------------- @content 将 @include中定义的样式引入到 @mixin 的 div 中
}
}
#app {
text-align: center;
color: #2c3e50;
margin-top: 60px;
.content {
padding: $safe-padding; // 使用全局scss样式
border: $border-1px;
@include test-mixin(yellow) { // --- @include,花括号中的内容在 @mixin中 可以通过 @content 来引用
font-size: 30px;
font-weight: 700;
}
}
}
</style>
复制代码
2
插值语句 #{}
- 通过 `#{}` 插值语句可以在 ( 选择器 ) 或 ( 属性名 ) 中使用 ( 变量 ):
---
$name: foo;
$attr: border;
p.#{$name} {
#{$attr}-color: blue;
}
编译为
p.foo {
border-color: blue;
}
复制代码
3
@at-root
- 将父级选择器直接暴力的改成根选择器,即和父级元素同级,而不是其子元素
---
.header{
@at-root {
.content{color:red}
}
}
编译为
.header{}
.content{color:red}
复制代码
4
@each
格式为 @each var in <list>|<map>
- var 表示任意变量名
- list 表示一连串的值
- map 表示 (a:b, c:d)
--------
list
@each $animal in puma, sea-slug, egret {
.#{$animal}-icon {}
}
编译为
.puma-icon {}
.sea-slug-icon { }
.egret-icon {}
--------
map
@each $header, $size in (h1: 2em, h2: 1.5em, h3: 1.2em) {
#{$header} {
font-size: $size;
}
}
编译为
h1 { font-size: 2em; }
h2 { font-size: 1.5em; }
h3 { font-size: 1.2em; }
复制代码
5
@for
格式为 `@for <variable> from <expression> to <expression> { ... }` ------ 不包含最后一个
或者为 `@for <variable> from <expression> through <expression> { ... }` - 包含最后一个
作用:从一个数字向上或者向下计数到另一个数字
区别:to不包含最后的数字,through包含最后的数字
---
@for $i from 1 through 4 { // 包含1-4个p都加上红色,to不包含最后一个
p:nth-child(#{$i}) {
background: red;
}
}
复制代码
6
math.div($number1, $number2)
返回 $number1 除以 $number2 的结果
---
@debug math.div(1, 2); // 0.5
@debug math.div(100px, 5px); // 20 单位相同
@debug math.div(100px, 5); // 20px 没有单位时
@debug math.div(100px, 5s); // 20px/s 单位不同时
复制代码
7
Sass中的Maps包含一对键值对,使得通过键查找值很容器
格式为:(<expression>: <expression>, <expression>: <expression>)
空map:用 () 表示
空列表:用 () 表示
Maps允许使用任何Sass值作为键
7.1 查值
map-get($map, $key)
$font-weights: ("regular": 400, "medium": 500, "bold": 700);
@debug map-get($font-weights, "medium"); // 500
@debug map-get($font-weights, "extra-bold"); // null
7.2 遍历
@each var in <list>|<map>
所以 @each是可以遍历map的
$icons: ("eye": "\f112", "start": "\f12e", "stop": "\f12f");
@each $name, $glyph in $icons { // 定义两个变量,相当于 key value
.icon-#{$name}:before {
content: $glyph;
}
} 编译为
.icon-eye:before { content: "\f112"; }
.icon-start:before {content: "\f12e"; }
.icon-stop:before { content: "\f12f"; }
7.3 添加元素
map-merge($map1, $map2)
- 如果两个map具有相同的键,则返回第二个键所对应的值
- 由于Sass中的map是[不可变的],`map-merge()`不会修改原始列表,所以 Sass的map函数都返回新的map,而不是修改原始的map
7.4 是否存在元素
map-has-key($map,$key)
- 返回一个布尔值
- 当 $map 中有这个 $key,则函数返回 true,否则返回 false
复制代码
(7) scss 变量有作用域
变量
- 分为 全局变量 和 局部变量
- 存在覆盖的情况
---
@mixin m($modifier) {
$selector: &; // 这里变量没有使用到 ?
$currentSelector: "";
@each $unit in $modifier {
$currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","};
}
// $currentSelector变量的初始值 ‘’ -> 会在循环中被覆盖掉
// - 例如
// - $currentSelector: "";
// - $currentSelector: .el-row-flex
@at-root {
#{$currentSelector} {
@content;
}
}
}
复制代码
(8) 双伪元素法 - 清除浮动
1 浮动元素
- 元素设置浮动后,( 浮动元素之前的元素不会受到影响 ),但是浮动元素之后的元素将围绕浮动的元素
2 清除浮动
- clear
- 清除浮动使用clear,用来指定 ( 该元素两侧不能出现浮动元素 )
- clear:both left right none inhert
- left表示左侧不能出现浮动元素
- 伪元素
- 伪元素的好处是不在DOM树中,提升性能
- BFC
- overflow:除去visible
- display:flex table-ceil
- 浮动
- 绝对定位
3 案例 // 清除浮动
@mixin utils-clearfix {
$selector: &;
@at-root {
#{$selector}::before,
#{$selector}::after {
display: table;
content: "";
}
#{$selector}::after {
clear: both
}
}
}
复制代码
(9) package.json
1
main module browser 三者在用webpack打包的优先级
- 浏览器环境
- browser > module > main
- node环境
- module > main
2
webpack如何指定打包的环境?
- 环境可以通过 webpack.config.js 中指定 taret
- target: web|node|electron-main|...
3
lib -> 一般是 commonjs 方式打包后存放的文件夹
es -> 一般是 esmoudle 方式打包后存放的文件夹
复制代码
(10) npm相关命令
(10.1) npm包版本号
---
版本号基本是由三位数字组成:
1 . 0 . 0 - beta.24
[MAJOR].[MINOR].[PATCH]-[alpha内部版本|beta公测版本|rc候选版本]
- MAJOR 进行不兼容的API更改时的版本 - 重大更新版本 不兼容
- MINOR 以向后兼容的方式添加功能时的版本 - 新功能 兼容
- PATCH 向后兼容的错误修复程序的版本 - 修复错误 兼容
// major 重要的 重大的
// minor 次要的 较小的
// patch 补丁
// alpha 希腊字母的第一个字母;开端;最初
// beta 希腊字母的第二个字母
复制代码
(10.2) ^ 和 ~ 的区别
~ 表示改变 path层级的版本
^ 表示改变 minor层级的版本
---
"vue": "2.5.21",
"vue": "~2.5.21" // 2.5.x ---------- ( 不低于2.5.21小于2.6.x )
"vue": "^2.5.21", // 2.x.x --------- ( 不低于2.5.21小于3.x.x )
复制代码
(10.3) 发布npm包相关的命令
---
1. 第一次发包
- npm adduser --- 注意 npm 必须是npm的源,不能是淘宝等,可以使用nrm切换
- 然后输入 用户名 密码
2. 非第一次发包
npm login
3. 发布
npm publish
4. 撤销发布的包
npm unpublish
npm unpublish 包名@版本号
5.1 更新至新的补丁版本
npm version patch
5.2 更新版本号并进行git提交,自定义提交描述
npm version major -m "版本更新至%s"
5.3 更新至新的补丁版本,并不增加git的tag
npm version patch --no-git-tag-version
复制代码
6. 引用模块 - 全局安装的包,在项目里面只需要引用即可
npm link
npm link [<@scope>/]<pkg>[@<version>]
7. 解除引用
npm unlink
npm link [<@scope>/]<pkg>[@<version>]
复制代码
(一) Row
(1.1) Row.js
// el-row
// 1
// name
// - 当element-ul在导出整个项目时是这样注册的 Vue.component(component.name, component);
// - Vue.component(id, [definition] )
// - 参数
// - {string} id 表示组件的唯一名字
// - {Function | Object} [definition] 表示组件
// - Vue.component('my-component', Vue.extend({ /* ... */ })) --> 注册组件,传入一个扩展过的构造器 --------> function
// - Vue.component('my-component', { /* ... */ }) --> 注册组件,传入一个选项对象 (自动调用 Vue.extend) ----> object
export default {
name: 'ElRow',
componentName: 'ElRow',
// componentName
// - 解释:componentName 是element-ui自己定义的属性
// - 作用:componentName 是为了在 el-col 中获取到 el-row,因为el-row在嵌套的情况下,需要寻找最新的el-row来配对
props: {
// tag ------ 自定义元素标签 string
// gutter --- 栅格间隔 number 0
// type ----- 布局模式,可选 flex,现代浏览器下有效 string
// justify -- flex 布局下的水平排列方式 start/end/center/space-around/space-between string
// align --- flex 布局下的垂直排列方式 top/middle/bottom string,重写了align-items
tag: {
type: String,
default: 'div'
},
gutter: Number,
type: String, // 当值是 'flex' 字符串时,表示flex布局方式
justify: {
type: String,
default: 'start'
},
align: String
},
computed: {
style() {
const ret = {};
if (this.gutter) {
ret.marginLeft = `-${this.gutter / 2}px`; // gutter 变化时从新计算 style
ret.marginRight = ret.marginLeft; // marginLeft 和 marginRight 值相等
}
return ret;
}
},
// 1
// render()
// - render函数签名:(createElement: () => VNode) => VNode
// 2
// createElement()
// 1. 返回值
// - createElement() 返回一个VNode
// 2. 参数
// - 第一个参数:
// - {String | Object | Function}
// - HTML标签名、组件选项对象(其实就是一个组件),或者 resolve 了上述任何一种的一个 async 函数。必填项。
// - 第二个参数
// - {Object}
// - 一个与模板中 attribute 对应的数据对象。可选。
// - 第二个参数其实就是数据对象,官网链接 https://cn.vuejs.org/v2/guide/render-function.html#createElement-%E5%8F%82%E6%95%B0
// - 从官网中我们需要学习到
// - 1. render和template相比的好处
// - 2. 第二个参数-即数据对象的属性有哪些,比如 class,style,attrs,props,domProps,on,nativeOn,directives,scopedSlots,slot,key,ref,refInFor
// - 3. render()方法中的一些约束
// - 第三个参数
// - {String | Array}
// - 子级虚拟节点 (VNodes),由 `crateElement()` 构建而成,也可以使用字符串来生成“文本虚拟节点”。可选
// - 参数注意点
// - 第二个和点三个参数是可选的
// - 当只有两个参数时,第二个参数会被当作第三个参数来处理
// 3
// row 相关的sass文件在 packages/theme-chalk/src/row.scss 文件中
// - chalk 是粉笔的意思
// 4
// 问题:这里为什么要用render,而不用template?
// 回答:因为 el-row 可以自定义标签的名称,如果使用template要很多if...else很冗余和麻烦
// 5
// el-row 中一共加了下面几个class
// - .el-row
// - .is-justify-
// - .is-align-
// - .el-row-flex 在type=== 'flex' 时存在
// 6
// style
// 默认的style,是计算属性style,把margin设置成了gutter的一半,方式el-row和rl-col左右两面有间距
render(h) {
return h(this.tag, { // 第一个参数:this.tag 是传入组件的tag属性,表示自定义该组件的元素标签
class: [ // 第二个参数:组件的attribute属性,就是传入组件的所有属性
'el-row',
this.justify !== 'start' ? `is-justify-${this.justify}` : '', // this.justify 默认值是 start
this.align ? `is-align-${this.align}` : '',
{ 'el-row--flex': this.type === 'flex' }
],
style: this.style // 计算属性,设置了 marginLeft和marginRight
}, this.$slots.default); // 第三个参数:表示通过 createElement创建的子节点,单个时string,多个时array
}
};
复制代码
(1.2) row scss
row scss
---
@import "common/var";
@import "mixins/mixins";
@import "mixins/utils";
// BEM
// 1
// b
// @mixin b($block) {
// $B: $namespace+'-'+$block !global;
// .#{$B} { // el-row
// @content;
// }
// }
// 2
// @mixin utils-clearfix
// 清除浮动
// @mixin utils-clearfix {
// $selector: &;
// @at-root {
// #{$selector}::before,
// #{$selector}::after {
// display: table;
// content: ""; // 不占大小
// }
// #{$selector}::after {
// clear: both // 清除浮动
// }
// }
// }
// 3
// @mixin m($modifier) {
// $selector: &;
// $currentSelector: "";
// @each $unit in $modifier {
// $currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","};
// }
// @at-root {
// #{$currentSelector} {
// @content;
// }
// }
// }
// 4
// @mixin when($state) {
// @at-root {
// &.#{$state-prefix + $state} { // is + $state
// @content;
// }
// }
// }
@include b(row) {
position: relative;
box-sizing: border-box;
@include utils-clearfix; // 清除浮动
// m
// 这里m的最终效果就是
// .el-row--flex: { display: flex; ...以及具体的 @when 中的布局方式 }
@include m(flex) {
display: flex;
&:before,
&:after {
display: none;
}
// when
// .el-row--flex .is-justify-center: { justify-content: center; }
// 其实上面之间是没有空格的,像下面这样
// .el-row--flex.is-justify-center: { justify-content: center; }
@include when(justify-center) {
justify-content: center;
}
@include when(justify-end) {
justify-content: flex-end;
}
@include when(justify-space-between) {
justify-content: space-between;
}
@include when(justify-space-around) {
justify-content: space-around;
}
@include when(align-top) {
align-items: flex-start;
}
@include when(align-middle) {
align-items: center;
}
@include when(align-bottom) {
align-items: flex-end;
}
}
}
复制代码
(1.3) minxin
@import "function";
@import "../common/var";
/* BEM
-------------------------- */
@mixin b($block) {
$B: $namespace + "-" + $block !global;
.#{$B} {
// 这里其实就是引用了 $B 变量做为class名 ---> el-$block,比如 el-row
@content;
}
// $B
// 1. 这里的 $namespace = el
// 2. $block 是参数,表示BEM中的block
// @content 是 @include 花括号中的值
// #{$B} 是插值语句
// - 插值语句可以在 选择器 或者 属性名 中使用 变量
}
@mixin m($modifier) {
$selector: &;
$currentSelector: "";
@each $unit in $modifier {
$currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","};
}
// $modifier-separator: '--';
// $namespace: 'el';
// $element-separator: '__';
// $state-prefix: 'is-';
// & --flex,
@at-root {
#{$currentSelector} {
@content;
}
}
}
@mixin when($state) {
@at-root {
&.#{$state-prefix + $state} {
@content;
}
}
// 1
// $state-prefix: 'is-';
}
/* Break-points
-------------------------- */
@mixin res($key, $map: $--breakpoints) {
// 循环断点Map,如果存在则返回
@if map-has-key($map, $key) {
@if $key=='sm-only'or $key=='md-only'or $key=='lg-only' {
// 判定特定定义处理字符串参数值问题
@media only screen and #{unquote(map-get($map, $key))} {
@content;
}
}
@else {
@media only screen and #{inspect(map-get($map, $key))} {
@content;
}
}
} @else {
@warn "Undefeined points: `#{$map}`";
}
}
复制代码
源码分析仓库
资料
- BEM规范 github.com/zhaotoday/b…
- element-ui源码解读 segmentfault.com/a/119000002…
- 浅谈element-ui中的BEM范式实践 www.cnblogs.com/ChengWuyi/p…
- render函数官网 cn.vuejs.org/v2/guide/re…
- 布局源码分析 juejin.cn/post/699778…
- browser moduel main优先级 zhuanlan.zhihu.com/p/129310389
- 热更新 www.manongjc.com/detail/23-n…
- 一分钟教你发布npm包 segmentfault.com/a/119000002…