[封装05-ElementUI源码01] Row Col Container Header Aside Main Footer

导航

[react] Hooks

[封装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] 二分查找和排序

[深入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

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 表示可选对象,可选
    • 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}`";
  }
}
复制代码

源码分析仓库

资料

猜你喜欢

转载自juejin.im/post/7042871115848351774
今日推荐