vuejs 源码学习第二天 这里用到的 vue 版本是 vue 目录 编译相关,把模版解析成抽象语法树 核心代码核心代码:1、内置组件,2、全局 API ,3、vue 实例化 ,4、观察者 5、虚拟 DOM 6、工具函数 不同平台的支持分别打包成运行在 weex 和 web 上的 vue.js 服务端渲染服务端渲染的代码 .vue 文件解析把.vue 文件解析成一个 javascript 对象 共…

vuejs 源码学习第二天

这里用到的 vue 版本是2.5.17-beta.0

vue 目录

  • vue/src/compiler

    编译相关,把模版解析成抽象语法树

  • vue/src/core

    核心代码核心代码:1、内置组件,2、全局 API ,3、vue 实例化 ,4、观察者 5、虚拟 DOM 6、工具函数

  • vue/src/paltforms

    不同平台的支持分别打包成运行在 weex 和 web 上的 vue.js

  • vue/src/server

    服务端渲染服务端渲染的代码

  • vue/src/sfc

    .vue 文件解析把.vue 文件解析成一个 javascript 对象

  • vue/src/shared

    共享代码定义一些服务端和浏览器端的 vue 的共享代码

vue/package.json 目录

  • main

    npm 包入口 import vue 通过 main 查找入口

  • module

    跟 main 类似

  • scripts

    定义很多脚步,这些脚本代表不同任务

vue.js 的构建打包过程

vuejs 是用 rollup,rollup 与 webpack 的区别是:webpack 会把图片等都打包成 js,而 rollup 只是构建 js

  • vue/scripts/config.js
  • vue/scripts/build.js

vue/scripts/config.js

  1. 从配置文件读取配置

    通过命令行参数对配置进行过滤

if (process.env.TARGET) {
  module.exports = genConfig(process.env.TARGET);
} else {
  exports.getBuild = genConfig;
  exports.getAllBuilds = () => Object.keys(builds).map(genConfig);
}
  1. builds 是什么

    builds 是不同版本 vuejs 的配置 genConfig 拿到 key 建立一个支持 rollup 的打包的配置结构

const builds = {
  'web-runtime-cjs': {
    entry: resolve('web/entry-runtime.js'), // 建入口 js 地址
    dest: resolve('dist/vue.runtime.common.js'), //构建后的 JS 文件地址
    format: 'cjs', // format 是构建格式,其中 cjs 是 commonJS、es 是 esmodule,umd 是 UMD 规范
    banner
  }
  ...
}
  1. resolve 是什么
// resolve 是映射到了不同的构建工具,
// 不同的映射工具可导出不同格式的 js,有 AMD的和CMD等
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);
  }
};
  1. genConfig是什么
// 生成rollup格式支持的配置
// 通过 object.keys(builds)拿到数组,genConfig 会构造出支持 rollup 打包的参数
function genConfig(name) {
  const opts = builds[name];
  const config = {
    input: opts.entry,
    external: opts.external,
    plugins: [
      replace({
        __WEEX__: !!opts.weex,
        __WEEX_VERSION__: weexVersion,
        __VERSION__: version
      }),
      flow(),
      buble(),
      alias(Object.assign({}, aliases, opts.alias))
    ].concat(opts.plugins || []),
    output: {
      file: opts.dest,
      format: opts.format,
      banner: opts.banner,
      name: opts.moduleName || 'Vue'
    }
  };

  if (opts.env) {
    config.plugins.push(
      replace({
        'process.env.NODE_ENV': JSON.stringify(opts.env)
      })
    );
  }

  Object.defineProperty(config, '_name', {
    enumerable: false,
    value: name
  });

  return config;
}

vue/scripts/build.js

config 生成完就该 build 了

  1. 判断打包方式
# 这个是scripts文件的打包入口
# 会根据参数判断是 web 打包方式还是 weex 打包方式
rollup -w -c scripts/config.js --environment TARGET:web-full-dev
rollup -w -c scripts/config.js --environment TARGET:weex-factory
// 这是判断哪种打包方式
let builds = require('./config').getAllBuilds();
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;
  });
}
  1. 进行编译
// 每次编译都要计数
build(builds);

function build(builds) {
  let built = 0;
  const total = builds.length;
  const next = () => {
    buildEntry(builds[built])
      .then(() => {
        built++;
        if (built < total) {
          next();
        }
      })
      .catch(logError);
  };

  next();
}
  1. buildEntry
// 获取 config 作为 rollup 编译的参数
function buildEntry(config) {
  const output = config.output;
  const { file, banner } = output;
  const isProd = /min\.js$/.test(file);
  return (
    rollup
      .rollup(config)
      // 产生 output
      .then((bundle) => bundle.generate(output))
      .then(({ code }) => {
        // 如果 isProd 为压缩的,执行 uglify 压缩
        if (isProd) {
          var minified =
            (banner ? banner + '\n' : '') +
            uglify.minify(code, {
              output: {
                ascii_only: true
              },
              compress: {
                pure_funcs: ['makeMap']
              }
            }).code;
          // write 是为了在编译的时候生成日志
          return write(file, minified, true);
        } else {
          // 否则直接 wrtie
          // write 是为了在编译的时候生成日志
          return write(file, code);
        }
      })
  );
}

runtime only 和 runtime+compiler 的区别

  1. runtime only

    是在编译阶段把.vue 文件编译成 js,只运行 vue.js 代码

  2. runtime+compiler

    如果在 vue 对象里面使用了 template,那就要实时编译,只写 render,可不用这种,.vue 的 template 是都通过 vue-loader 转成成 render 函数,所以跟这个没关系。

import vue 的过程

以 Runtime + Compiler 方式为例

  1. 进入src/platforms/web/entry-runtime-with-compiler.js
  2. 导入import Vue from './runtime/index'
  3. 再导入import Vue from 'core/index'

    导入的同时,也执行了 initGlobalAPI(Vue)

export function initGlobalAPI(Vue: GlobalAPI) {
  // config
  const configDef = {};
  configDef.get = () => config;
  if (process.env.NODE_ENV !== 'production') {
    configDef.set = () => {
      warn('Do not replace the Vue.config object, set individual fields instead.');
    };
  }
  Object.defineProperty(Vue, 'config', configDef);

  // exposed util methods.
  // NOTE: these are not considered part of the public API - avoid relying on
  // them unless you are aware of the risk.
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  };

  Vue.set = set;
  Vue.delete = del;
  Vue.nextTick = nextTick;

  Vue.options = Object.create(null);
  ASSET_TYPES.forEach((type) => {
    Vue.options[type + 's'] = Object.create(null);
  });

  // this is used to identify the "base" constructor to extend all plain-object
  // components with in Weex's multi-instance scenarios.
  Vue.options._base = Vue;

  extend(Vue.options.components, builtInComponents);
  // 挂载了很多静态的方法
  initUse(Vue);
  initMixin(Vue);
  initExtend(Vue);
  initAssetRegisters(Vue);
}
  1. import Vue from './instance/index'
// 最终Vue是 ES5 的 Function 实例
// 为什么不用 ES6 的 class 方法去构造 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);
}
// 定义了很多 mixin 的原型方法对vue 对象进行扩展
initMixin(Vue);
stateMixin(Vue);
eventsMixin(Vue);
lifecycleMixin(Vue);
renderMixin(Vue);

暂无评论