zoukankan      html  css  js  c++  java
  • vue源码构建代码分析

    这是xue源码学习记录,如有错误请指出,谢谢!相互学习相互进步。

    vue源码目录为

    
    vue
    ├── src        #vue源码    
    ├── flow       #flow定义的数据类型库(vue通过flow来检测数据类型是否正确)         
    ├── examples   #demo   
    ├── scripts    #vue构建命令
    ├── ...    
    

    vue内部代码模块比较清晰,这边主要分析scripts内部代码,讲解vue是如何进行构建的.
    首先你必须要懂一些rollup,vue内部是通过rollup来进行构建的,rollup是一款js的构建工具,
    将各个小模块打包成一个总的模块(只针对js文件,比较轻量,不会有css,img等压缩,比较适合开发插件,
    如果是ui组件库的话,还是webpack构建会比较好。)
    rollup说明文档:https://rollupjs.cn/

    文件主要是scripts下的alias.js,config.js和build.js三个文件组成

    alias

    主要就是提供文件对应的路径

    
    const path = require('path')
    
    const resolve = p => path.resolve(__dirname, '../', p)
    
    // 以下是设置别名,与对应的真实文件路径
    module.exports = {
      vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
      compiler: resolve('src/compiler'),
      core: resolve('src/core'),
      shared: resolve('src/shared'),
      web: resolve('src/platforms/web'),
      weex: resolve('src/platforms/weex'),
      server: resolve('src/server'),
      entries: resolve('src/entries'),
      sfc: resolve('src/sfc'),
      test: resolve('src/test') // 这个是测试目录是自己添加的
    }
    

    其中test是我自己加的,为了测试打包

    config

    config是为了提供打包的基础配置(即rollup打包配置文件格式),由于打包内容比较多,
    所以做成可配置的

    
    const path = require('path')
    const buble = require('rollup-plugin-buble')
    // 提供modules名称的 alias 和reslove 功能
    const alias = require('rollup-plugin-alias')
    // 将CommonJS模块转换为 ES2015供Rollup 处理
    const cjs = require('rollup-plugin-commonjs')
    // 变量替换,可以将动态设置的变量提取出来在配置文件中设置
    const replace = require('rollup-plugin-replace')
    // 帮助 Rollup 查找外部模块,然后安装
    const node = require('rollup-plugin-node-resolve') 
    const flow = require('rollup-plugin-flow-no-whitespace')
    const version = process.env.VERSION || require('../package.json').version
    // 下面是weex服务器端代码,不需要管
    // const weexVersion = process.env.WEEX_VERSION || require('../packages/weex-vue-framework/package.json').version
    
    // 这边是打包完成后模块外部首行注释代码
    const banner = 
      `/*
       * test-vue.js v${version}
       * (C) 2014-${new Date().getFullYear()} Enan You
       * @author zhengjie
       */
      `
    // 获取文件夹路径别名
    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)
      }
    }
    // 设置打包参数 
    // 这边把原先vue打包文件去除,替换成知己的一个测试文件
    const builds = {
      'test-cjs': {
        entry: resolve('test/main.js'),
        dest: resolve('dist/test-cjs.js'),
        format: 'cjs', // csj格式 module.exports
        banner
      },
      'test-es': {
        entry: resolve('test/main.js'),
        dest: resolve('dist/test-es.js'),
        format: 'es', // es格式 export default
        banner
      },
      'test-umd': {
        entry: resolve('test/main.js'),
        dest: resolve('dist/test-umd.js'),
        format: 'umd', // 浏览器格式 return
        banner
      }
    }
    
    // 根据上面builds打包转换成rollup打包格式
    function getConfig(name) {
      const opts = builds[name]
      const config = {
        input: opts.entry,
        external: opts.external,
        plugins: [
          replace({
            __WEEX__: !!opts.weex,
            __WEEX_VERSION__: null,
            __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
    }
    
    if (process.env.TARGET) {
      module.exports = getConfig(process.env.TARGET)
    } else {
      exports.getBuild = getConfig
      exports.getAllBuilds = () => Object.keys(builds).map(getConfig)
    }
    
    

    其中里面的builds已被替换成我自己的测试文件,用于测试打败es,模块化和浏览器的不同格式。

    build

    build文件就是根据配置文件进行打包,打包模式分为全部打包,或者是可配置打包,
    如果运行npm run build,将会打包所有的配置,
    而运行npm run build '参数', 则根据参数配置进行打包

    
    const fs = require('fs')
    const path = require('path')
    const zlib = require('zlib')
    const rollup = require('rollup')
    const uglify = require('uglify-js')
    // 检测是否有dist文件
    if (!fs.existsSync('dist')) {
      fs.mkdirSync('dist')
    }
    
    let builds = require('./config').getAllBuilds()
    
    // build后面输入的参数
    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 {
      // 这边过滤出weex,不需要管
      builds = builds.filter(b => {
        return b.output.file.indexOf('weex') === -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()
    }
    // 单个配置文件打包
    function buildEntry(config) {
      const output = config.output
      const {file, banner} = output
      // 是否为压缩文件
      const isProd = /min.js$/.test(file)
      return rollup.rollup(config)
        .then(bundle => bundle.generate(output))
        .then(({code}) => {
          // 压缩我文件
          if (isProd) {
            var minified = (banner ? banner + '
    ' : '') + uglify.minify(code, {
              output: {
                ascii_only: true
              },
              compress: {
                pure_funcs: ['makeMap']
              }
            }).code
            return write(file, minified, true)
          } else {
            return write(file, code)
          }
        })
    }
    
    function write(dest, code, zip) {
      return new Promise((resolve, reject) => {
        function report (extra) {
          console.log(blue(path.relative(process.cwd(), dest)) + ' ' + getSize(code) + (extra || ''))
          resolve()
        }
    
        fs.writeFile(dest, code, err => {
          if (err) return reject(err)
          if (zip) {
            zlib.gzip(code, (err, zipped) => {
              if (err) return reject(err)
              report(' (gzipped: ' + getSize(zipped) + ')')
            })
          } else {
            report()
          }
        })
      })
    }
    // 计算文件大小
    function getSize (code) {
      return (code.length / 1024).toFixed(2) + 'kb'
    }
    // 输入错误信息
    function logError (e) {
      console.log(e)
    }
    function blue (str) {
      return 'x1b[1mx1b[34m' + str + 'x1b[39mx1b[22m'
    }
    

    测试文件

    main.js

    
    import foo from './foo';
    export default function () {
      console.log(foo)
    }
    

    foo.js

    
    export default "hello rollup"
    

    通过npm run build打包测试,会在dist文件打包出test-cjs.js,test-es.js,test-umd.js
    文件内容如下:
    test-cjs.js

    
    /*
       * test-vue.js v1.0.0
       * (C) 2014-2018 Enan You
       * @author zhengjie
       */
      
    'use strict';
    
    var foo = "hello rollup"
    
    function main () {
      console.log(foo);
    }
    
    module.exports = main;
    

    test-es.js

    
    /*
       * test-vue.js v1.0.0
       * (C) 2014-2018 Enan You
       * @author zhengjie
       */
      
    var foo = "hello rollup"
    
    function main () {
      console.log(foo);
    }
    
    export default main;
    
    

    test-umd.js

    
    /*
       * test-vue.js v1.0.0
       * (C) 2014-2018 Enan You
       * @author zhengjie
       */
      
    (function (global, factory) {
        typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
        typeof define === 'function' && define.amd ? define(factory) :
        (global.Vue = factory());
    }(this, (function () { 'use strict';
    
    var foo = "hello rollup"
    
    function main () {
      console.log(foo);
    }
    
    return main;
    
    })));
    
    

    这个构建的过程不难,比起webpack的配置文件要容易懂很多
    懂得了vue的构建,接下来就可以开始vue源码的学习了

    原文地址:

  • 相关阅读:
    Oracle分页问题
    win10系统vs2008环境wince项目无法创建问题
    工作满十年了
    让Vs2013 完美支持EF6.1 Code First with Oracle 2015年12月24日更新
    Oracle DMP 操作笔记之根据DMP逆向推导出导出的表空间名称
    【转】如何在 Eclipse 中進行 TFS 的版本管控
    【转】什麼是 Team Explorer Everywhere 2010 ?TFS 專用的 Eclipse 整合套件的安裝與設定
    [转]有关USES_CONVERSION
    [转]使用VC/MFC创建一个线程池
    IT男的”幸福”生活
  • 原文地址:https://www.cnblogs.com/lalalagq/p/9901166.html
Copyright © 2011-2022 走看看