zoukankan      html  css  js  c++  java
  • vue create 初步解析以及定制化修改

    版本说明

    $ vue --version
    @vue/cli 4.5.9
    $ node --version
    v14.0.0
    $ npm --version
    7.6.1
    

    源码位置-mac

    /usr/local/lib/node_modules/@vue/cli
    

    流程说明

    1. 根据package.json中的bin可以知道入口文件在bin/vue.js
    2. vue.js中找到create <app-name>的命令设定,在action钩子中调用了lib/create

    create.js

    1. 获取项目绝对路径
    2. 判断项目文件夹是否存在
    3. 创建文件夹
    4. 调用lib/Creator.js

    Creator.js

    Creator.create()

    1. 根据创建项目时的选择项配置preset插件组

    2. 深度拷贝preset

    3. 配置preset中的默认插件的配置项:@vue/cli-service@vue/cli-plugin-router@vue/cli-plugin-typescript@vue/cli-plugin-vuex

    4. 获取包管理器:yarn/npm/pnpm

    5. 开始创建项目,获取vue-cli版本

    6. 根据preset中的插件配置生成package.json文件

    7. pnpm生成.npmrc文件,为yarn生成.yarnrc

    8. 为项目添加git配置:git init

    9. 安装CLI插件:生成指定格式的文件目录

      await generator.generate({
        extractConfigFiles: preset.useConfigFiles,
      });
      
    10. 唤醒插件

    11. 安装配置额外的依赖

    12. running completion hooks

    13. 生成README.md

    14. 配置git的状态,为测试配置Git

    15. 完成

    自定义修改脚手架程序

    公司内项目所使用的插件等配置基本相似,在项目开发过程中也有很多提高开发效率的做法,这些可以在脚手架的程序流程中自定义

    1. 摆脱复制-粘贴的重复工作,同时避免复制-粘贴丢失导致的可以不用debug的bug
    2. 提高项目开发效率,统一项目开发风格

    生成vue.config.js-仿生成README.md

    1. generateVueConfig.js
    // 生成README.md:lib/util/generateReademe.js
    // 生成vue.config.js:lib/util/generateVueConfig.js,我另有其他配置项,需要根据配置生成vue.config.js
    module.exports = function generateVueConfig(plugins) {
      const iconSvg = plugins['svg-sprite-loader'] ? `chainWebpack: config => {
        // svg rule loader
        const svgRule = config.module.rule('svg') // 找到svg-loader
        svgRule.uses.clear() // 清除已有的loader, 如果不这样做会添加在此loader之后
        svgRule.exclude.add(/node_modules/) // 正则匹配排除node_modules目录
        svgRule // 添加svg新的loader处理
          .test(/\.svg$/)
          .use('svg-sprite-loader')
          .loader('svg-sprite-loader')
          .options({
            symbolId: 'icon-[name]',
          })
    
        // 修改images loader 添加svg处理
        const imagesRule = config.module.rule('images')
        imagesRule.exclude.add(resolve('src/icon/svg'))
        config.module
          .rule('images')
          .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
      }` : ''
    
      return `const path = require('path')
    
    function resolve(dir) {
      return path.join(__dirname, './', dir)
    }
    module.exports = {
      publicPath: './',
      devServer: {
        proxy: {
          '/api': {
            target: 'http://localhost:5000',
            changeOrigin: true
          }
        }
      },
      productionSourceMap: false,
      ${iconSvg}
    }`
    }
    
    1. 修改create流程-Creator.js

      // 自定义生成vue.config.js,写入多次使用的配置,比如跨域配置,可以直接写,也可以将内容写在另一个js文件中并引入
      if (!generator.files['vue.config.js']) {
        log()
        log('⚙\u{fe0f}  Generating vue.config.js...')
        await writeFileTree(context, {
          'vue.config.js': generateVueConfig(preset.otherPlugins),
        })
      }
      

    配置并引入自定义npm包-仿配置@vue/cli-xxx包

    在开发中,有一些包总会被引入,比如axios,加密的包,UI框架包等,可以在vue create前期选择时将其加入,来生成代码。

    引入@vue/cli-xxx包流程:

    1. create.js中,根据new Creator来进入整体流程,初始化Creator时,传入了初始包列表,以下仅摘要了重要代码
    // create.js
    const { getPromptModules } = require('./util/createTools')
    const creator = new Creator(name, targetDir, getPromptModules())
    // getPromptModules()
    exports.getPromptModules = () => {
      return [
        'vueVersion',
        'babel',
        'typescript',
        'pwa',
        'router',
        'vuex',
        'cssPreprocessors',
        'linter',
        'unit',
        'e2e'
      ].map(file => require(`../promptModules/${file}`))
    }
    
    
    1. 初始化Creator时,调用了PromptModuleAPI引入了相关包,调用了相关包的配置命令lib/promptModules/xxx
    // Creator.js
    constructor(name, context, promptModules) {
      const promptAPI = new PromptModuleAPI(this);
      promptModules.forEach((m) => m(promptAPI));
      // 以上命令执行后,在shell界面中会显示每个相关包的配置命令,此时使用者进行选择或输入
    }
    
    1. 在Creator.create()方法中,根据preset为每一个包配置了配置项,存储在preset.plugins
    // Creator.js-create()方法,以下仅举例 
    preset.plugins["@vue/cli-service"] = Object.assign(
      {
        projectName: name,
      },
      preset
    );
    
    if (cliOptions.bare) {
      preset.plugins["@vue/cli-service"].bare = true;
    }
    
    // legacy support for router
    if (preset.router) {
      preset.plugins["@vue/cli-plugin-router"] = {};
    
      if (preset.routerHistoryMode) {
        preset.plugins["@vue/cli-plugin-router"].historyMode = true;
      }
    }
    

    仿制以上流程即可导入自己想在初始化过程中导入的包,比如axios等,为避免冲突,全部为另开发的代码,以下以axios以及可选择式的UI框架来举例

    1. createOtherTools.js -仿制getPromptModules函数,配置自定义包列表,并添加到初始化Creator中
    // lib/util/createOtherTools.js  otherModules文件夹存储自定义包的相关命令行
    exports.getOtherPromptModules = () => {
      return [
        'axios',
        'uiStruct'
      ].map(file => require(`../otherModules/${file}`))
    }
    // create.js
    const { getPromptModules } = require('./util/createTools')
    const { getOtherPromptModules } = require('./util/createOtherTools')
    const creator = new Creator(name, targetDir, getPromptModules(), getOtherPromptModules())
    
    1. 导入相关包的命令行配置,将自定义包的配置存储在options.otherPlugins
    // Creator.js
    constructor(name, context, promptModules, otherPromptModules) {
      const promptAPI = new PromptModuleAPI(this);
      promptModules.forEach((m) => m(promptAPI));
      otherPromptModules.forEach((m) => m(promptAPI));
      // 以上命令执行后,在shell界面中会显示每个相关包的配置命令,此时使用者进行选择或输入
    }
    // 新建 otherModules 文件夹
    // otherModules/axios.js
    module.exports = cli => {
      cli.injectFeature({
        name: 'Axios',  // 显示在vue create命令后的选择项里的名字
        value: 'axios',  // 相对应的value值
        description: 'Promise based HTTP client for the browser and node.js',  // 介绍
        link: 'https://github.com/mzabriskie/axios',  // 相关链接
        checked: true  // 显示在vue create命令后的选择项里,是否默认选中
      })
    
      cli.onPromptComplete((answers, options) => {
        if (answers.features.includes('axios')) {  // includes(value)是否包含上面定义的value值
          options.otherPlugins['axios'] = {}  // 在此指定相对应的报名
        }
      })
    }
    // otherModules/uiStruct.js
    module.exports = cli => {
      cli.injectFeature({
        name: 'Choose UI Struct',  // 显示在vue create命令后的选择项里的名字
        value: 'ui-struct',  // 相对应的value值
        description: 'Choose a struct of UI that you want to use with the project'  // 介绍
      })
    
      cli.injectPrompt({
        name: 'uiStruct',
        when: answers => answers.features.includes('ui-struct'),  // 判断是否选择
        message: 'Choose a struct of UI that you want to use with the project',  // 描述
        type: 'list',  // 选择类shell命令
        choices: [
          {
            name: 'ant-design-vue',  // 选项名
            value: 'ant-design-vue'  // 选项相对应的包名
          },
          {
            name: 'element-ui',
            value: 'element-ui'
          }
        ],
        default: 'element-ui'  // 默认选项
      })
    
      cli.onPromptComplete((answers, options) => {
        if (answers.uiStruct) {
          options.otherPlugins[answers.uiStruct] = {}  // answers.uiStruct存储了包名
        }
      })
    }
    
    
    1. 将自定义包的引入加入到流程中,在生成package.json之前引入
    // Creator.js - create()函数
    if (preset.otherPlugins) {
      Object.keys(preset.otherPlugins).forEach((dep) => {
        let { version } = preset.otherPlugins[dep];
        pkg.dependencies[dep] = version ? version : "latest";
      });
    }
    
    ฅ平平庸庸的普通人ฅ
  • 相关阅读:
    [分治FFT]「CTSC2018」青蕈领主
    [边分治+线段树合并]「CTSC2018」暴力写挂
    [模板]MTT
    [模板]NTT
    [矩阵求逆+二分图匹配]BZOJ 3168 [Heoi2013]钙铁锌硒维生素
    [BZOJ1925][SDOI2010]地精部落(DP)
    [BZOJ1047][HAOI2007]理想的正方形(RMQ+DP)
    [POJ3630]Phone List (Tire)
    [POJ1193][NOI1999]内存分配(链表+模拟)
    [POJ2823]Sliding Window 滑动窗口(单调队列)
  • 原文地址:https://www.cnblogs.com/fengzzi/p/15736798.html
Copyright © 2011-2022 走看看