美文网首页前端学习打卡
Vue源码——准备工作

Vue源码——准备工作

作者: 沫小司 | 来源:发表于2020-05-13 22:21 被阅读0次

    源码目录

    Vue.js 的源码都在 src 目录下,结构如下:

    src
    ├── compiler        # 编译相关 (模板解析成 ast 语法树及优化)可以构建时也可以运行时
    ├── core            # 核心代码 (内置组件、全局 API 封装,Vue 实例化、观察者、虚拟 DOM、工具函数等)
    ├── platforms       # 不同平台的支持(入口:web和weex)
    ├── server          # 服务端渲染(跑在服务端的 Node.js)
    ├── sfc             # .vue 文件解析
    ├── shared          # 共享代码(浏览器端和服务端的共享工具方法)
    

    源码构建

    Vue.js 源码是基于 Rollup 构建的,它的构建相关配置都在 scripts 目录下。

    构建脚本

    通常一个npm托管项目都会有个package.json文件,它是对项目的描述文件,内容为一个标准JSON对象。
    通常会配置 script 字段作为 NPM 的执行脚本:

    {
      "script": {
        "build": "node scripts/build.js",
        "build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
        "build:weex": "npm run build -- weex"
      }
    }
    

    可以看到这三条命令都是通过script/build.js构建 vue.js ,不同的是后面两条命令增加了环境参数。

    构建过程

    script/build.js中:

    let builds = require('./config').getAllBuilds()
    
    // filter builds via command line arg
    if (process.argv[2]) {
      const filters = process.argv[2].split(',')
      builds = builds.filter(b => {
        return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1)
      })
    } else {
      // filter out weex builds by default
      builds = builds.filter(b => {
        return b.output.file.indexOf('weex') === -1
      })
    }
    
    build(builds)
    

    首先读取config.js中的配置,然后通过命令行参数做配置过滤。
    然后看一下script/confit.js,以web-runtime-cjs为例:

    const builds = {
      // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
      'web-runtime-cjs': {
        entry: resolve('web/entry-runtime.js'),//入口
        dest: resolve('dist/vue.runtime.common.js'),//目标文件
        format: 'cjs',//构建格式
        banner
      }
    }
    

    构建格式分为:

    格式名称 遵循规范
    cjs CommonJS
    es ES Module
    umd UMD

    entry看起,它的entryresolve(‘web/entry-runtime.js’),那么看一下resolve方法:

    const aliases = require('./alias')
    const resolve = p => {
      const base = p.split('/')[0]
      if (aliases[base]) {
        return path.resolve(aliases[base], p.slice(base.length + 1))
      } else {
        return path.resolve(__dirname, '../', p)
      }
    }
    

    它将传入参数通过/分割为数组,取第一个元素设置为base,此时base并不是实际路径,而是别名。
    可以看到这里别名在script/alias中:

    const path = require('path')
    
    module.exports = {
      vue: path.resolve(__dirname, '../src/platforms/web/entry-runtime-with-compiler'),
      compiler: path.resolve(__dirname, '../src/compiler'),
      core: path.resolve(__dirname, '../src/core'),
      shared: path.resolve(__dirname, '../src/shared'),
      web: path.resolve(__dirname, '../src/platforms/web'),
      weex: path.resolve(__dirname, '../src/platforms/weex'),
      server: path.resolve(__dirname, '../src/server'),
      entries: path.resolve(__dirname, '../src/entries'),
      sfc: path.resolve(__dirname, '../src/sfc')
    }
    

    这样就可以通过对应目录找到真实的入口路径。
    ../src/platforms/web/entry-runtime.js,经过 Rollup 的构建打包,最终在dist目录下生成vue.runtime.common.js

    Runtime Only 和 Runtime+Compiler

    当用 vue-cli 去初始化Vue项目时会询问用Runtime Only 版本的还是 Runtime + Compiler 版本,对比一下:

    • Runtime Only

    需要使用 webpack 的 vue-loader 工具将.vue文件编译为JavaScript。由于做了预编译,所以它只包含运行代码,因此代码体积更轻量。

    • Runtime + Compiler

    如果没有对代码做预编译,但又使用了 Vue 的 template 属性并传入一个字符串,则需要在客户端编译模板。

    可以看下两种版本:

    // 需要编译器的版本
    new Vue({
      template: '<div>{{ hi }}</div>'
    })
    
    // 这种情况不需要
    new Vue({
      render (h) {
        return h('div', this.hi)
      }
    })
    

    由于最终渲染都是通过render函数,因此当写了templete属性时就需要编译。
    由于编译的性能损耗,因此更推荐使用Runtime-Only 的 Vue.js。

    相关文章

      网友评论

        本文标题:Vue源码——准备工作

        本文链接:https://www.haomeiwen.com/subject/fkfonhtx.html