精华内容
下载资源
问答
  • 1、私有过滤器filter如何定义 通过Vue的过滤器方法Vue.filter('过滤器名', function(args){})进行定义 定义私有过滤器的语法如下: new Vue({ el: '.app', data: {}, filters: { 'chgLowerCase': ...

    1、私有过滤器filters如何定义

    通过Vue的过滤器方法Vue.filter('过滤器名', function(args){})进行定义

    定义私有过滤器的语法如下:

    new Vue({
    		el: '.app',
    		data: {},
    		filters: {
    			'chgLowerCase': function(val){
    				return val.toLowerCase();
    			},
    			'setNumber': function(val){
    				return val.replace(/[0-4]/g, '6');
    			},
    		},
    	})
    

    2、如何调用私有过滤器filters

    定义成功后,在需要使用的地方(过滤变量)使用{{ user|toLowerCase }}进行调用

    3、实例代码演示

    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<title>02_vue中私有过滤器的使用</title>
    </head>
    <body>
    	<div class="app">
    		用户名:<input type="text" v-model.lazy.trim="user" /><br />
    		数  字:<input type="text" v-model.lazy.trim="number" /><br />
    		<span>{{ user|chgLowerCase }}</span><br />
    		<span>{{ number|setNumber }}</span>
    	</div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
    <script>
    	// 私有过滤器的使用
    	// 第一:创建vue实例对象的时候,通过filters属性定义过滤器的名字以及回调函数
    	// 第二:定义成功了后,在需要使用的地方(过滤变量)使用{{ user|toLowerCase }}进行调用
    	
    	// Vue.filter('toLowerCase', function(val){
    	// 	return val.toLowerCase();
    	// })
    
    	var app = new Vue({
    		el: '.app',
    		data: {
    			user: '',
    			number: '',
    		},
    		filters: {
    			'chgLowerCase': function(val){
    				return val.toLowerCase();
    			},
    			'setNumber': function(val){
    				return val.replace(/[0-4]/g, '6');
    			},
    		},
    	})
    </script>
    </html>
    
    展开全文
  • vue 调用后接口实现Excel数据导入功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建...

    HTML 导入按钮部分

    导入
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210618110407134.png)

    js部分
    mounted() {
    this.KaTeX parse error: Expected 'EOF', got '}' at position 99: …e); }) }̲, methods:{ …/.test(files[0].name.toLowerCase())){
    this.$Message.error(‘上传格式不正确,请上传xls或者xlsx格式’);
    return false;
    }

          let param = new FormData();
          //通过append向form对象添加数据
          param.append("file", e.target.files[0]);
          //FormData私有类对象,访问不到,可以通过get判断值是否传进去
          //console.log(param.get("file"));
          let config = {
            //添加请求头
            headers: {"Content-Type": "application/json;"},
          };
          axios.post('/in/inexamstandardfour/upload',param, config).then(
            (response) => {
              //console.log(response);
              if (response.code = 200) {
                this.dialogShowAlert = true;
                this.alertMsg = response.message;
    
    			//页面表格table部分数据接口调用,封装后的,这里就不具体写了
                this.getData({
                  pageNum: this.pageNum,
                  pageSize: this.pageSize
                });
              }
            })
    
    
        },
    

    }
    在这里插入图片描述
    在这里插入图片描述

    我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

    1. 全新的界面设计 ,将会带来全新的写作体验;
    2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
    3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
    4. 全新的 KaTeX数学公式 语法;
    5. 增加了支持甘特图的mermaid语法1 功能;
    6. 增加了 多屏幕编辑 Markdown文章功能;
    7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
    8. 增加了 检查列表 功能。

    功能快捷键

    撤销:Ctrl/Command + Z
    重做:Ctrl/Command + Y
    加粗:Ctrl/Command + B
    斜体:Ctrl/Command + I
    标题:Ctrl/Command + Shift + H
    无序列表:Ctrl/Command + Shift + U
    有序列表:Ctrl/Command + Shift + O
    检查列表:Ctrl/Command + Shift + C
    插入代码:Ctrl/Command + Shift + K
    插入链接:Ctrl/Command + Shift + L
    插入图片:Ctrl/Command + Shift + G
    查找:Ctrl/Command + F
    替换:Ctrl/Command + G

    合理的创建标题,有助于目录的生成

    直接输入1次#,并按下space后,将生成1级标题。
    输入2次#,并按下space后,将生成2级标题。
    以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

    如何改变文本的样式

    强调文本 强调文本

    加粗文本 加粗文本

    标记文本

    删除文本

    引用文本

    H2O is是液体。

    210 运算结果是 1024.

    插入链接与图片

    链接: link.

    图片: Alt

    带尺寸的图片: Alt

    居中的图片: Alt

    居中并且带尺寸的图片: Alt

    当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

    如何插入一段漂亮的代码片

    博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

    // An highlighted block
    var foo = 'bar';
    

    生成一个适合你的列表

    • 项目
      • 项目
        • 项目
    1. 项目1
    2. 项目2
    3. 项目3
    • 计划任务
    • 完成任务

    创建一个表格

    一个简单的表格是这么创建的:

    项目Value
    电脑$1600
    手机$12
    导管$1

    设定内容居中、居左、居右

    使用:---------:居中
    使用:----------居左
    使用----------:居右

    第一列第二列第三列
    第一列文本居中第二列文本居右第三列文本居左

    SmartyPants

    SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

    TYPEASCIIHTML
    Single backticks'Isn't this fun?'‘Isn’t this fun?’
    Quotes"Isn't this fun?"“Isn’t this fun?”
    Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

    创建一个自定义列表

    Markdown
    Text-to- HTML conversion tool
    Authors
    John
    Luke

    如何创建一个注脚

    一个具有注脚的文本。2

    注释也是必不可少的

    Markdown将文本转换为 HTML

    KaTeX数学公式

    您可以使用渲染LaTeX数学表达式 KaTeX:

    Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

    Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

    你可以找到更多关于的信息 LaTeX 数学表达式here.

    新的甘特图功能,丰富你的文章

    Mon 06 Mon 13 Mon 20 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
    • 关于 甘特图 语法,参考 这儿,

    UML 图表

    可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:

    张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

    这将产生一个流程图。:

    链接
    长方形
    圆角长方形
    菱形
    • 关于 Mermaid 语法,参考 这儿,

    FLowchart流程图

    我们依旧会支持flowchart的流程图:

    Created with Raphaël 2.3.0 开始 我的操作 确认? 结束 yes no
    • 关于 Flowchart流程图 语法,参考 这儿.

    导出与导入

    导出

    如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

    导入

    如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
    继续你的创作。


    1. mermaid语法说明 ↩︎

    2. 注脚的解释 ↩︎

    展开全文
  • this._init做了什么 _init 方法是在调用 initMixin(Vue) 方法时挂载在 Vue 的原型上的,入口:src/core/instance/init.js // 1. uid +1 vm._uid = uid++ // 2. a flag to avoid this being observed vm._isVue = ...

    数据驱动

    概念

    数据驱动指的是:

    视图是由数据驱动生成的,我们对视图的修改,不会直接操作 DOM,而是通过修改数据。

    只关心数据的修改会让代码的逻辑变的非常清晰,因为 DOM 变成了数据的映射,我们所有的逻辑都是对数据的修改,而不用碰触 DOM,这样的代码非常利于维护。

    new Vue 过程

    Vue 类的定义入口:src/core/instance/index.js

    // 创建了 Vue 类
    function Vue (options) {
      if (process.env.NODE_ENV !== 'production' &&
        !(this instanceof Vue)
      ) {
        warn('Vue is a constructor and should be called with the `new` keyword')
      }
      this._init(options)
    }
    
    // 1. Vue 初始化,原型添加 _init 函数
    initMixin(Vue)
    // 2. 定义状态,原型添加 $set,$delete,$watch 函数
    stateMixin(Vue)
    // 3. 定义事件,原型添加 $on,$once,$off,$emit
    eventsMixin(Vue)
    // 4. 定义生命周期函数,原型添加 _update,$forceUpdate,$destroy
    lifecycleMixin(Vue)
    // 5. 定义渲染函数,原型添加 $nextTick,_render 函数
    renderMixin(Vue)
    

    Vue 只能通过 new 关键字初始化,然后调用 this._init(options)

    this._init做了什么

    _init 方法是在调用 initMixin(Vue) 方法时挂载在 Vue 的原型上的,入口:src/core/instance/init.js

    // 1. uid +1
    vm._uid = uid++
    
    // 2. a flag to avoid this being observed
    vm._isVue = true
    
    // 3. 合并配置
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    
    // 4. 将 vm(this) 挂载在私有变量_self 上
    vm._self = vm
    // 5. 初始化一些变量:生命周期(里面初始化一些生命周期要用到的私有变量)、事件、渲染
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    
    // 6. 调用钩子函数 beforeCreate
    callHook(vm, 'beforeCreate')
    
    initInjections(vm) // resolve injections before data/props
    
    // 7. 初始化配置 $options 上的属性状态:props,methods,data,computed,watch
    initState(vm)
    
    initProvide(vm) // resolve provide after data/props
    
    //8. 调用钩子函数 created
    callHook(vm, 'created')
    
    // 9. 初始化的最后,如果有 el 属性则调用 vm.$mount 方法挂载 vm
    // 挂载的目标就是把模板渲染成最终的 DOM
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
    

    vm.$mount 做了什么

    入口:src/platforms/web/entry-runtime-with-compiler.js

    在这个文件里重新定义了 $mount 原型方法(编译版才有,runtime-only 版本没有)

    const mount = Vue.prototype.$mount
    Vue.prototype.$mount = function (
      el?: string | Element,
      hydrating?: boolean // 与服务端渲染相关
    ): Component {
      // ...
    
      // 调用 compileToFunctions 进行在线编译,转换成 render 方法
      const { render, staticRenderFns } = compileToFunctions(template, {
           shouldDecodeNewlines,
           shouldDecodeNewlinesForHref,
           delimiters: options.delimiters,
           comments: options.comments
       }, this)
       options.render = render
       options.staticRenderFns = staticRenderFns
          
      return mount.call(this, el, hydrating)
    }
    

    第一次定义的入口:src/platforms/web/runtime/index.js

    Vue.prototype.$mount = function (
      el?: string | Element,
      hydrating?: boolean
    ): Component {
      el = el && inBrowser ? query(el) : undefined
      // hydrating 和服务端渲染相关,在浏览器环境下不需要传第二个参数
      return mountComponent(this, el, hydrating)
    }
    

    mountComponent 函数入口:src/core/instance/lifecycle.js

    拆解为以下步骤:

    // 1. 挂载 el 元素到 $el
    vm.$el = el
    
    // 2. 执行钩子函数 beforeMount
    callHook(vm, 'beforeMount')
    
    // 3. 定义 updateComponent 函数,这个函数先执行了 vm._render() 方法生成虚拟 Node(VNode),再调用 vm._update 更新 DOM
    let updateComponent
    updateComponent = () => {
    	vm._update(vm._render(), hydrating)
    }
    
    // 4. 实例化一个 渲染 Watcher (重点)
    // Watch 作用:1.初始化的时候会执行回调函数 2. 当 vm 实例中的监测的数据发生变化的时候执行回调函数
    new Watcher(vm, updateComponent, noop, {
      before () {
        if (vm._isMounted && !vm._isDestroyed) {
          callHook(vm, 'beforeUpdate')
        }
      }
    }, true /* isRenderWatcher */)
    hydrating = false
    
    if (vm.$vnode == null) {
      // 判断为根节点的时候设置 vm._isMounted 为 true,表示这个实例已经挂载了
      vm._isMounted = true
      // 5. 执行钩子函数 mounted
      callHook(vm, 'mounted')
    }
    

    因此接下来我们看 vm._render 方法做了什么。

    vm._render 做了什么

    入口:src/core/instance/render.js

    _render 方法是实例的一个私有方法,把实例渲染成一个虚拟 Node。

    Vue.prototype._render = function (): VNode {
      const vm: Component = this
      const { render, _parentVnode } = vm.$options
      
      // reset _rendered flag on slots for duplicate slot check
      if (process.env.NODE_ENV !== 'production') {
        for (const key in vm.$slots) {
          // $flow-disable-line
          vm.$slots[key]._rendered = false
        }
      }
      
      if (_parentVnode) {
        vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject
      }
      
      // 设置父节点,使得渲染函数可以取得父节点的 data 数据
      vm.$vnode = _parentVnode
      
      // render self
      let vnode
      try {
        // render 方法的调用
        // vm.$createElement 创建元素,返回 VNode : https://cn.vuejs.org/v2/guide/render-function.html#createElement-%E5%8F%82%E6%95%B0
        vnode = render.call(vm._renderProxy, vm.$createElement)
      } catch (e) {
        handleError(e, vm, `render`)
        // return error render result,
        // or previous vnode to prevent render error causing blank component
        /* istanbul ignore else */
        if (process.env.NODE_ENV !== 'production') {
          if (vm.$options.renderError) {
            try {
              vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
            } catch (e) {
              handleError(e, vm, `renderError`)
              vnode = vm._vnode
            }
          } else {
            vnode = vm._vnode
          }
        } else {
          vnode = vm._vnode
        }
      }
      
      // return empty vnode in case the render function errored out
      if (!(vnode instanceof VNode)) {
        if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
          warn(
            'Multiple root nodes returned from render function. Render function ' + 'should return a single root node.',
            vm
          )
        }
        vnode = createEmptyVNode()
      }
      
      // set parent
      vnode.parent= _parentVnode
      return vnode
    }
    

    什么是 VNode(虚拟 DOM)

    在调试控制台通过 document.createElement 创建的原生 DOM 元素,可以看到这个 DOM 元素上的属性和方法繁多,非常庞大。如下图所示。

    Virtual DOM 是使用原生 JS 对象去描述一个 DOM 节点。比起创建一个原生 DOM 元素来说,代价要小得多。

    // 例如
    // DOM 是这样:
    <div class="test">
        <p>123</p>
    </div>
    
    // VNode 将真实的 DOM 的数据抽取出来,以对象的形式模拟树形结构。
    var Vnode = {
        tag: 'div',
        data:{
    	    class: 'test'
        },
        children: [
            { tag: 'p', text: '123' }
        ]
    };
    

    Vue.js 中定义了一个 VNode 类。

    入口:src/core/vdom/vnode.js

    
    export default class VNode {
      tag: string | void; // 当前节点的标签名
      data: VNodeData | void; // 当前节点的数据对象。VNodeData 定义入口: types/vnode.d.ts 
      children: ?Array<VNode>; // 数组类型,包含了当前节点的子节点
      text: string | void; // 当前节点的文本,一般文本节点或注释节点会有该属性
      elm: Node | void; // 当前虚拟节点对应的真实的dom节点
      ns: string | void; // 当前节点的 namespace 命名空间
      context: Component | void; // 当前节点的编译作用域
      key: string | number | void; // 节点的key属性,用于作为节点的标识,有利于patch的优化
      componentOptions: VNodeComponentOptions | void; // 创建组件实例时会用到的选项信息
      componentInstance: Component | void; // 当前节点对应的组件实例
      parent: VNode | void; // 当前节点的父节点
    
      // strictly internal
      raw: boolean; // 判断是否为HTML或普通文本,innerHTML的时候为true,innerText的时候为false (仅服务端)
      isStatic: boolean; // 是否为静态节点
      isRootInsert: boolean; // 是否作为根节点插入,被<transition>包裹的节点,该属性的值为false
      isComment: boolean; // 当前节点是否是注释节点
      isCloned: boolean; // 当前节点是否为克隆节点
      isOnce: boolean; // 当前节点是否有v-once(只渲染一次)指令
      asyncFactory: Function | void; // 异步组件工厂函数
      asyncMeta: Object | void;
      isAsyncPlaceholder: boolean;
      ssrContext: Object | void;
      fnContext: Component | void; // 函数化组件的作用域
      fnOptions: ?ComponentOptions; // SSR 缓存
      devtoolsMeta: ?Object; // 用于存储 devtools 的渲染函数上下文
      fnScopeId: ?string; // 提供的函数作用域 Id
    
      constructor (
        tag?: string,
        data?: VNodeData,
        children?: ?Array<VNode>,
        text?: string,
        elm?: Node,
        context?: Component,
        componentOptions?: VNodeComponentOptions,
        asyncFactory?: Function
      ) {
        this.tag = tag
        this.data = data
        this.children = children
        this.text = text
        this.elm = elm
        this.ns = undefined
        this.context = context
        this.fnContext = undefined
        this.fnOptions = undefined
        this.fnScopeId = undefined
        this.key = data && data.key
        this.componentOptions = componentOptions
        this.componentInstance = undefined
        this.parent = undefined
        this.raw = false
        this.isStatic = false
        this.isRootInsert = true
        this.isComment = false
        this.isCloned = false
        this.isOnce = false
        this.asyncFactory = asyncFactory
        this.asyncMeta = undefined
        this.isAsyncPlaceholder = false
      }
    
      // (已淘汰) DEPRECATED: 向后兼容的 componentInstance 的别名。
      /* istanbul ignore next */
      get child (): Component | void {
        return this.componentInstance
      }
    }
    

    Vue.jsVirtual DOM 的实现借鉴了一个开源库:snabbdom

    由于 VNode 只是用来映射到真实 DOM 的渲染,不需要包含操作 DOM 的方法,因此它是非常轻量和简单的。

    上面只是说了 Virtual DOM 类的数据结构,那么它要如何映射到真实的 DOM?

    映射经历了三个过程:create(创建)、diff(比较)、patch(打补丁)

    VNode 在 Vue.js 中如何创建

    入口:src/core/vdom/create-element.js

    // 0 : 不需要进行规范化
    const SIMPLE_NORMALIZE = 1 // 1:只需要简单的规范化处理,将 children 数组打平一层并且都规范为 VNode 类型
    const ALWAYS_NORMALIZE = 2 // 2:完全规范化,将一个 N 层的 children 完全打平为一维数组且规范为 VNode 类型
    
    function createElement (
      context: Component,
      tag: any,
      data: any,
      children: any,
      normalizationType: any,
      alwaysNormalize: boolean
    ): VNode | Array<VNode> {
      // 若 data 是数组 || data 是四种基本类型(string/number/symbol/boolean)
      if (Array.isArray(data) || isPrimitive(data)) {
        normalizationType = children
        children = data
        data = undefined
      }
      if (isTrue(alwaysNormalize)) {
        normalizationType = ALWAYS_NORMALIZE
      }
      return _createElement(context, tag, data, children, normalizationType)
    }
    

    createElement 函数将参数处理完成后,调用 _createElement 进行 VNode 真正的创建。

    function _createElement (
      context: Component, // VNode 的上下文环境,是 Component 类型
      tag?: string | Class<Component> | Function | Object, // 表示标签
      data?: VNodeData, // 表示 VNode 的数据,它是一个 VNodeData 类型
      children?: any, // children 表示当前 VNode 的子节点,它是任意类型的,它将被规范为标准的 VNode 数组
      normalizationType?: number // 表示子节点规范的类型
    ): VNode | Array<VNode> {
      // isDef 判断参数是否 defined (!undefined&&!null)
      // 并且data的__ob__已经定义(表示已经被 observed,上面绑定了 Oberver 对象)
      // 则创建一个空节点
      if (isDef(data) && isDef((data: any).__ob__)) {
        process.env.NODE_ENV !== 'production' && warn(
          `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
          'Always create fresh vnode data objects in each render!',
          context
        )
        return createEmptyVNode()
      }
      
      // is: 用于动态组件且基于 DOM 内模板的限制来工作。https://cn.vuejs.org/v2/api/#is
      if (isDef(data) && isDef(data.is)) {
        tag = data.is
      }
      if (!tag) {
        // 万一动态组件的 :is 被设为假值,则返回一个没有内容的注释节点
        return createEmptyVNode()
      }
      
      // 警告:当 key 值非基本类型(string/number/symbol/boolean)
      if (process.env.NODE_ENV !== 'production' &&
        isDef(data) && isDef(data.key) && !isPrimitive(data.key)
      ) {
        if (!__WEEX__ || !('@binding' in data.key)) {
          warn(
            'Avoid using non-primitive value as key, ' +
            'use string/number value instead.',
            context
          )
        }
      }
      
      // 支持单个函数子节点作为默认作用域插槽
      if (Array.isArray(children) &&
        typeof children[0] === 'function'
      ) {
        data = data || {}
        data.scopedSlots = { default: children[0] }
        children.length = 0
      }
      
      if (normalizationType === ALWAYS_NORMALIZE) {
        // 完全规范化(将 children 完全打成一维)
        children = normalizeChildren(children)
      } else if (normalizationType === SIMPLE_NORMALIZE) {
        // 简单规范化(将 children 降一个维度)
        children = simpleNormalizeChildren(children)
      }
      
      let vnode, ns
      // 如果 tag 标签是 string 类型的,即 div/p/span 之类的。
      if (typeof tag === 'string') {
        let Ctor
        // 定义标签名的命名空间
        ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
        
        // 判断 tag 标签是否是 HTML 的保留标签
        if (config.isReservedTag(tag)) {
          // 则直接创建一个普通 VNode 节点
          vnode = new VNode(
            config.parsePlatformTagName(tag), data, children,
            undefined, undefined, context
          )
          
        } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
          // 判断 tag 是否为自定义组件标签 
          
          // Ctor 为组件的构造类
          // 创建一个组件节点
          vnode = createComponent(Ctor, data, context, children, tag)
          
        } else {
          // 未知命名空间的元素
          // 也要兜底地创建一个节点
          // check at runtime because it may get assigned a namespace when its
          // parent normalizes children
          vnode = new VNode(
            tag, data, children,
            undefined, undefined, context
          )
        }
      } else {
        // tag 不是字符串时直接创建一个组件节点
        vnode = createComponent(tag, data, context, children)
      }
      
      if (Array.isArray(vnode)) {
        return vnode
      } else if (isDef(vnode)) {
        if (isDef(ns)) applyNS(vnode, ns)
        if (isDef(data)) registerDeepBindings(data)
        return vnode
      } else {
        // 如果vnode没有成功创建则创建空节点
        return createEmptyVNode()
      }
    }
    

    什么是 children 规范化

      if (normalizationType === ALWAYS_NORMALIZE) {
        // 完全规范化(将 children 完全打成一维)
        children = normalizeChildren(children)
      } else if (normalizationType === SIMPLE_NORMALIZE) {
        // 简单规范化(将 children 降一个维度)
        children = simpleNormalizeChildren(children)
      }
    

    问题: 为什么在这里要对 children 子节点们进行规范?
    解答:
    VNode 实际上是一个树状结构,每一个 VNode 可能会有若干个子节点,这些子节点也应该是 VNode 结构。
    但是在 createElement 函数中, children 的类型是 any,也就是说可以是任意类型的。
    因此, 必须将其规范为 VNode 类型。

    那再来看normalizeChildrensimpleNormalizeChildren分别做了什么事。

    函数入口:src/core/vdom/helpers/normalize-children.js

    // 代码中对需要两个规范化做出的解释:
    // 模板编译器试图在编译时静态分析模板来最大程度地减少规范化的次数。
    // 对于纯 HTML 标记,可以完全跳过规范化,因为可以保证所生成的渲染函数返回的数组元素都是是 VNode 类型。
    // 但这里有两个额外的场景是需要规范化的:
    
    function simpleNormalizeChildren (children: any) {
      for (let i = 0; i < children.length; i++) {
        if (Array.isArray(children[i])) {
          return Array.prototype.concat.apply([], children)
        }
      }
      return children
    }
    
    // 2. 当子节点始终包含嵌套的数组(多维数组)时 -> 两种情况:
    //    a. 当编译 <template>, <slot>, v-for 时会产生多维数组
    //    b. 当用户自己手写渲染函数/JSX,子节点可能是多维数组 
    // 以上两种情况都需要通过完全规范化来适配所有可能出现的情况
    function normalizeChildren (children: any): ?Array<VNode> {
      // 1. 如果 children 是基本类型,则创建文本节点
      // 2. 如果是一个数组,则调用 normalizeArrayChildren() 函数
      // 3. 否则返回 undefined
      return isPrimitive(children)
        ? [createTextVNode(children)]
        : Array.isArray(children)
          ? normalizeArrayChildren(children)
          : undefined
    }
    
    /**
     * normalizeArrayChildren
     * @param {*} children 表示要规范的子节点
     * @param {*} nestedIndex 表示嵌套的索引, 因为 children 内部的单个 child 也可能是一个数组
     */
    function normalizeArrayChildren (children: any, nestedIndex?: string): Array<VNode> {
      const res = [] // 存放最后展平的 VNode 数组
      let i, c, lastIndex, last
      // 遍历 children
      for (i = 0; i < children.length; i++) {
        // 获得单个节点 c
        c = children[i]
        // 若 c 节点为 undefined 或 boolean 类型,则下一个
        if (isUndef(c) || typeof c === 'boolean') continue
        
        lastIndex = res.length - 1
        last = res[lastIndex]
        //  如果是一个数组类型
        if (Array.isArray(c)) {
          if (c.length > 0) {
            // 递归调用 normalizeArrayChildren
            c = normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`)
            
            // 合并相邻的文本节点
            if (isTextNode(c[0]) && isTextNode(last)) {
              res[lastIndex] = createTextVNode(last.text + (c[0]: any).text)
              c.shift()
            }
            res.push.apply(res, c)
          }
        } else if (isPrimitive(c)) { // 若是基本类型
          // 如果已部分展平的 VNode 数组 res 的最后一个节点是文本节点
          //(即存在两个连续的 text 节点,会把它们合并成一个 text 节点)
          if (isTextNode(last)) {
            // 合并相邻的文本节点
            // 这在服务端渲染里是很有必要的,因为服务端渲染为 html 字符串类型时,文本节点为字符串类型,本质上就需要合并
            res[lastIndex] = createTextVNode(last.text + c)
          } else if (c !== '') { // 若未空字符串
            // 将 c 转换为文本节点
            res.push(createTextVNode(c))
          }
        } else {
          // 如果当前节点和已存入的最后一个节点都是文本节点
          if (isTextNode(c) && isTextNode(last)) {
            // 合并相邻的文本节点
            res[lastIndex] = createTextVNode(last.text + c.text)
          } else {
            // 可能是由 v-for 生成的带有默认 key 的嵌套数组子节点
            if (isTrue(children._isVList) &&
              isDef(c.tag) &&
              isUndef(c.key) &&
              isDef(nestedIndex)) {
              c.key = `__vlist${nestedIndex}_${i}__`
            }
            res.push(c)
          }
        }
      }
      return res
    }
    

    vm._update 做了什么

    入口:src/core/instance/lifecycle.js

    _update 方法是实例的一个私有方法,它会在(1)首次渲染,(2)数据更新时调用,这个方法的作用是把 VNode 渲染成真实的 DOM。

    这里先对首次渲染部分进行分析,数据更新在分析响应式原理时再看。

    Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
      const vm: Component = this
      const prevEl = vm.$el
      const prevVnode = vm._vnode
      const prevActiveInstance = activeInstance
      activeInstance = vm
      vm._vnode = vnode
      
      // Vue.prototype.__patch__ 方法是根据使用的渲染后端不同在入口处引入的方法不相同
      // 即不同的平台,例如在 web 和 weex 上的定义不一样
      if (!prevVnode) {
        // initial render
        vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
      } else {
        // updates
        vm.$el = vm.__patch__(prevVnode, vnode)
      }
      
      activeInstance = prevActiveInstance
      // update __vue__ reference
      if (prevEl) {
        prevEl.__vue__ = null
      }
      if (vm.$el) {
        vm.$el.__vue__ = vm
      }
      // if parent is an HOC, update its $el as well
      if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
        vm.$parent.$el = vm.$el
      }
      // updated hook is called by the scheduler to ensure that children are
      // updated in a parent's updated hook.
    }
    

    从上面的源码看,核心方法就是 __patch__,这个方法也是整个Virtual DOM构建真实DOM最核心的方法。

    Vue.prototype.__patch__

    函数功能: 主要完成了新的虚拟节点和旧的虚拟节点的diff过程,根据两者的比较结果最小单位地修改视图。经过patch过程之后生成真实的 DOM 节点并完成视图更新的工作。

    入口:src/platforms/web/runtime/index.js

    Vue.prototype.__patch__ = inBrowser ? patch : noop
    

    可以看到,在非浏览器环境(服务端渲染)中,是一个空函数。

    原因:在服务端渲染中,没有真实的浏览器 DOM 环境,不需要把 VNode 最终转换成 DOM。

    在浏览器环境中,是一个 patch 方法。

    patch函数定义的入口:src/platforms/web/runtime/patch.js

    import * as nodeOps from 'web/runtime/node-ops' // 封装了一系列 DOM 操作的方法
    import { createPatchFunction } from 'core/vdom/patch'
    import baseModules from 'core/vdom/modules/index' // 要引入的基础模块
    import platformModules from 'web/runtime/modules/index' // 根据平台不同引入不同的模块
    
    // the directive module should be applied last, after all
    // built-in modules have been applied.
    const modules = platformModules.concat(baseModules)
    
    export const patch: Function = createPatchFunction({ nodeOps, modules })
    

    createPatchFunction 定义的入口: src/core/vdom/patch.js

    const hooks = ['create', 'activate', 'update', 'remove', 'destroy']
    
    function createPatchFunction (backend) {
    
      let i, j
      const cbs = {}
    
      const { modules, nodeOps } = backend
    
      for (i = 0; i < hooks.length; ++i) {
        cbs[hooks[i]] = []
        for (j = 0; j < modules.length; ++j) {
          if (isDef(modules[j][hooks[i]])) {
            cbs[hooks[i]].push(modules[j][hooks[i]])
          }
        }
      }
    
      // 定义一系列辅助方法
      // ... 
    
      /**
       * 最终返回了一个 patch 方法
       * @param {*} oldVnode 表示旧的 VNode 节点, 可以不存在或者是一个 DOM 对象
       * @param {*} vnode 表示执行 _render 后返回的 VNode 的节点
       * @param {*} hydrating 表示是否是服务端渲染
       * @param {*} removeOnly 给 transition-group 用
       */
      return function patch (oldVnode, vnode, hydrating, removeOnly) {
        // 执行 _render 后返回的 vnode 不存在
        if (isUndef(vnode)) {
          // 旧的 VNode 节点存在则销毁旧的 VNode 的钩子
          if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
          return
        }
    
        let isInitialPatch = false
        const insertedVnodeQueue = []
        
        // oldVnode 未定义的时候
        if (isUndef(oldVnode)) {
          // 说明是一个空的 mount(可能是组件),创建一个新的 root 元素
          // empty mount (likely as component), create new root element
          isInitialPatch = true
          createElm(vnode, insertedVnodeQueue)
        } else {
          const isRealElement = isDef(oldVnode.nodeType)
          if (!isRealElement && sameVnode(oldVnode, vnode)) {
            // 是位置处在同一个节点则直接调用 patchVnode 函数修改现有节点
            patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
          } else {
            if (isRealElement) {
              // mounting to a real element
              // check if this is server-rendered content and if we can perform
              // a successful hydration.
              // 当旧的 VNode 是服务端渲染的元素,hydrating 标记为 true
              if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {
                oldVnode.removeAttribute(SSR_ATTR)
                hydrating = true
              }
              
              // oldVnode 是服务端渲染的元素
              if (isTrue(hydrating)) {
                // hydrate 函数只支持浏览器,则可以将 oldVnode 节点理解为真实 DOM 节点
                // 将渲染得到的新节点 vnode 合并进 DOM 节点 oldVnode
                if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {
                  invokeInsertHook(vnode, insertedVnodeQueue, true)
                  return oldVnode
                } else if (process.env.NODE_ENV !== 'production') {
                  warn(
                    'The client-side rendered virtual DOM tree is not matching ' +
                    'server-rendered content. This is likely caused by incorrect ' +
                    'HTML markup, for example nesting block-level elements inside ' +
                    '<p>, or missing <tbody>. Bailing hydration and performing ' +
                    'full client-side render.'
                  )
                }
              }
              // either not server-rendered, or hydration failed.
              // create an empty node and replace it
              // 如果 oldVnode 是真实节点但上面的操作又不是服务器端渲染或是合并到真实DOM失败,
              // 则创建一个空节点替换 oldVnode
              oldVnode = emptyNodeAt(oldVnode)
            }
    
            // replacing existing element
            const oldElm = oldVnode.elm
            const parentElm = nodeOps.parentNode(oldElm)
    
            // create new node
            // 虚拟节点创建真实 DOM 并插入到它的父节点中
            createElm(
              vnode,
              insertedVnodeQueue,
              // 这里有一个极其罕见的边缘情况
              // extremely rare edge case: do not insert if old element is in a
              // leaving transition. Only happens when combining transition +
              // keep-alive + HOCs. (#4590)
              oldElm._leaveCb ? null : parentElm,
              nodeOps.nextSibling(oldElm)
            )
    
            // update parent placeholder node element, recursively
            // 递归地更新父节点 element
            if (isDef(vnode.parent)) {
              let ancestor = vnode.parent
              const patchable = isPatchable(vnode)
              while (ancestor) {
                for (let i = 0; i < cbs.destroy.length; ++i) {
                  cbs.destroy[i](ancestor)
                }
                ancestor.elm = vnode.elm
                if (patchable) {
                  for (let i = 0; i < cbs.create.length; ++i) {
                    cbs.create[i](emptyNode, ancestor)
                  }
                  // #6513
                  // invoke insert hooks that may have been merged by create hooks.
                  // e.g. for directives that uses the "inserted" hook.
                  const insert = ancestor.data.hook.insert
                  if (insert.merged) {
                    // start at index 1 to avoid re-invoking component mounted hook
                    for (let i = 1; i < insert.fns.length; i++) {
                      insert.fns[i]()
                    }
                  }
                } else {
                  registerRef(ancestor)
                }
                ancestor = ancestor.parent
              }
            }
    
            // destroy old node
            if (isDef(parentElm)) {
              // 如果父节点存在,则移除旧节点
              removeVnodes([oldVnode], 0, 0)
            } else if (isDef(oldVnode.tag)) {
              // 调用 destroy 钩子
              invokeDestroyHook(oldVnode)
            }
          }
        }
    
        invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
        return vnode.elm
      }
    }
    
    展开全文
  • Vue中定义私有过滤器

    2020-04-07 21:42:25
    人在,塔在 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>私有过滤器<...script src="../../vue/vue.js"></script> </hea...

    人在,塔在

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>私有过滤器</title>
        <script src="../../vue/vue.js"></script>
    
    </head>
    <body>
    <div id="app">
        <p>{{ dt | dateFormat('yyyy-mm-dd')}}</p>
    </div>
    </body>
    <script>
        var vm = new Vue({
           el: '#app',
           data: {
               dt: new Date()
           },
           filters: { //当全局过滤和私有过滤器同时存在时,遵循就近原则 (调用私有的过滤器)
    
               // 第一个参数是管道符(|)前面的值,第二个参数是自定义的
               dateFormat(dateStr, pattern='') {
                   var y = dateStr.getFullYear();
                   //因为月份是从0开始计算的所以得加1
                   var m = dateStr.getMonth() + 1;
                   var d = dateStr.getDate();
    
                   if (pattern.toLowerCase() === 'yyyy-mm-dd') {
                       return `${y}-${m}-${d}`
                   }else {
                       var hh = dateStr.getHours();
                       var mm = dateStr.getMinutes();
                       var ss = dateStr.getSeconds();
                       return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
                   }
               }
           }
        });
    </script>
    </html>
    
    展开全文
  • Vue的设置私有自定义指令的方法如下 directives: {'指令名称': { inserted: function(elem){ } } options说明: ‘指令名称’ --自定义指令名称 inserted --代表回调函数引用名称; function(elem){} --代表...
  • 过滤器应被添加在JavaScript表达式的尾部 由“管道”符指示(管道符就是一个竖杠:|) 一、全局过滤器 全局过滤器调用时的基本格式: 通过管道符[|]来进行过滤 例如{{name | nameOpe}} 定义过滤器的语法:Vue.filter(...
  • vue组件 vue两大思想:数据驱动界面、组件思想 组件 必须在vm对象创建之前注册组件(全局) Vue.component("bts",{ template:"<div><h1>this.mytitle</h1><p>bts</p></div&...
  • Vue.js
  • vue.js

    2020-03-17 23:25:41
    vue.js基础 Vue.js是一个轻量级MVVM框架。vue.js的核心思想是数据驱动+组件化。 1.vue实例 <script src="../lib/vue.js"></script> <body> <div id="app"> 你的名字是:{{name}} </div...
  • 组件的出现 拆分了Vue实例的代码量 能够以不同的组件来划分不同的功能模块 需要什么样的功能 调用对应的组件即可 组件化和模块化的区别: 模块化: 从代码逻辑的角度进行划分的 方便了代码的分层开发 保证了每个...
  • 一、自定义全局指令 开门见山 举个栗子: <label> 搜索 <input type="text" id="search"> </label>...此时 若要触发这个输入框的获得焦点事件 在...而在Vue中 使用自定义指令来实现该效果 指令 ...
  • data是私有数据,主要供组件内部访问。 vue作为一种前端开发框架,组件是其中的主角吧。其实任何一种前端框架,除了vue,还有react,组件都应该是主角。为啥呢,因为组件最能体现面向对象思想,单一职责,良好的封装...
  • 概念:Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示; 私有过滤器 HTML...
  • Vue.js

    2019-03-21 16:24:25
    Vue.js相关文章vue.js 1.x 文档vue.js 2.x 文档String.prototype.padStart(maxLength, fillString)js 里面的键盘事件对应的键码Vue.js双向绑定的实现原理Vue.js devtools - 翻墙安装方式 - 推荐自定义指令vue.js ...
  • Vue.js2模板工程说明 目的 封装常用功能和配置,规范代码编写,保证输出的一致性,方便前端人员在此基础上快速开发新项目的业务模块。 下载 模板项目(Boilerplate)分为JavaScript和TypeScript两个版本。 JS: git ...
  • 初识 Vue.js

    2021-03-27 00:26:27
    Vue是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面...
  • 为了提升代码的可维护性,Vue 3.x 的源码全部采用 TypeScript 编写 大型项目的开发都推荐使用类型化的语言,在编码的过程当中帮我们检查类型的问题 使用 Monorepo 管理项目结构 把独立的功能模块都提取到不同的...
  • 模拟 Vue.js 响应式原理2.1 数据驱动2.2 数据响应式的核心原理Vue 2.x - definePropertyVue 3.x - Proxy2.3 发布订阅模式和观察者模式发布/订阅模式观察者模式发布订阅/观察者模式 总结2.4 2.1 数据驱动 在学习 Vue...
  • Vue.js 创建Vue组件的方式

    千次阅读 2019-02-24 17:10:12
    文章目录定义Vue组件全局组件定义的三种方式定义实例内部私有组件 定义Vue组件 什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能...
  • 过滤器应被添加在JavaScript表达式的尾部 由“管道”符指示(管道符就是一个竖杠:|) 一、全局过滤器 全局过滤器调用时的基本格式: 通过管道符[|]来进行过滤 例如{{name | nameOpe}} 定义过滤器的语法:Vue.filter...
  • 全局过滤器是所有Vue实例都能调用,而本节的私有过滤器,就是定义在Vue实例中的过滤器: var vm2 = new Vue({ el: '#app2', data: {}, methods: {}, filters: { // 定义私有过滤器,过滤器有两个条件【过滤器...
  • Vue.js基础知识

    2021-04-02 15:54:02
    一、Vue.js是什么? 它是一套用于构建用户界面的渐进式框架。其设计为自底向上逐层应用。Vue只关心视图层。 二、声明式的渲染 1、 Vue.js的核心是采用简洁的模板语法来声明式地将数据渲染到DOM系统 <div id...
  • 封装 Vue.js 组件库组件化开发基础概念处理组件的边界情况$attrs / $listeners 组件化开发 基础概念 CDD CDD(Component-Driven Development) 自上而下 从组件级别开始,到页面级别结束 先从相对完的的设计中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,700
精华内容 2,680
关键字:

vue私有调用js

vue 订阅