<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          vue-cli 基本原理

          共 7769字,需瀏覽 16分鐘

           ·

          2021-08-31 09:04


          編寫一個簡單的 vue-cli 就可以輕松明白原理是怎么運行的了。理解 cli 的插件形式開發(fā),并且可以選擇配置,下載不同的模板內(nèi)容。


          const inquirer = require('inquirer')const path = require('path')const fs = require('fs-extra')const execa = require('execa')const Module = require('module')const ejs = require('ejs')
          const isManualMode = answers => answers.preset === '__manual__'const context = path.resolve(__dirname, 'my-app') // 假設要輸出到 my-app 文件const name = 'my-app' // vue create my-app
          const isString = val => typeof val === 'string'const isFunction = val => typeof val === 'function'const isObject = val => val && typeof val === 'object'
          const promptCompleteCbs = [ // (answers, options) => { // if (answers.features.includes('vuex')) { // options.plugins['@vue/cli-plugin-vuex'] = {} // } // }]
          const defaultPreset = { useConfigFiles: false, cssPreprocessor: undefined, plugins: { '@vue/cli-plugin-babel': {}, '@vue/cli-plugin-eslint': { config: 'base', lintOn: ['save'] } }}
          const presets = { 'default': Object.assign({ vueVersion: '2' }, defaultPreset), '__default_vue_3__': Object.assign({ vueVersion: '3' }, defaultPreset)}
          const presetChoices = Object.entries(presets).map(([name, preset]) => { let displayName = name if (name === 'default') { displayName = 'Default' } else if (name === '__default_vue_3__') { displayName = 'Default (Vue 3)' } return { name: `${displayName}`, value: name }})
          const presetPrompt = { name: 'preset', type: 'list', message: `Please pick a preset:`, choices: [ ...presetChoices, { name: 'Manually select features', value: '__manual__' } ]}let features = [ 'vueVersion', 'babel', 'typescript', 'pwa', 'router', 'vuex', 'cssPreprocessors', 'linter', 'unit', 'e2e']
          const featurePrompt = { name: 'features', when: isManualMode, type: 'checkbox', message: 'Check the features needed for your project:', choices: features, pageSize: 10}
          const prompts = [ presetPrompt, featurePrompt]
          function run (command, args) { return execa(command, args, { cwd: context })}
          function loadModule (request, context) { return Module.createRequire(path.resolve(context, 'package.json'))(request)}
          async function resolvePlugins (rawPlugins, pkg) { const plugins = [] for (const id of Object.keys(rawPlugins)) { const apply = loadModule(`${id}/generator`, context) || (() => {}) let options = rawPlugins[id] || {} plugins.push({ id, apply, options }) } return plugins}
          function extractCallDir () { // extract api.render() callsite file location using error stack const obj = {} Error.captureStackTrace(obj) const callSite = obj.stack.split('\n')[3]
          // the regexp for the stack when called inside a named function const namedStackRegExp = /\s\((.*):\d+:\d+\)$/ // the regexp for the stack when called inside an anonymous const anonymousStackRegExp = /at (.*):\d+:\d+$/
          let matchResult = callSite.match(namedStackRegExp) if (!matchResult) { matchResult = callSite.match(anonymousStackRegExp) }
          const fileName = matchResult[1] return path.dirname(fileName)}
          function renderFile (name, data, ejsOptions) { const template = fs.readFileSync(name, 'utf-8')
          let finalTemplate = template.trim() + `\n`
          return ejs.render(finalTemplate, data, ejsOptions)}
          async function writeFileTree (dir, files) { Object.keys(files).forEach((name) => { const filePath = path.join(dir, name) fs.ensureDirSync(path.dirname(filePath)) fs.writeFileSync(filePath, files[name]) })}
          class GeneratorAPI { constructor (id, generator, options, rootOptions) { this.id = id this.generator = generator this.options = options this.rootOptions = rootOptions this.pluginsData = generator.plugins } _injectFileMiddleware (middleware) { this.generator.fileMiddlewares.push(middleware) } _resolveData (additionalData) { return Object.assign({ options: this.options, rootOptions: this.rootOptions, plugins: this.pluginsData }, additionalData) } extendPackage (fields, options = {}) { // 合并兩個package } render (source, additionalData = {}, ejsOptions = {}) { const baseDir = extractCallDir() console.log(source, 'source') if (isString(source)) { source = path.resolve(baseDir, source) // 找到了插件的tempalte目錄 // 放到 fileMiddlewares 數(shù)組里面去,并沒有執(zhí)行中間件 this._injectFileMiddleware(async (files) => { const data = this._resolveData(additionalData) const globby = require('globby') const _files = await globby(['**/*'], { cwd: source, dot: true }) // 模板里面是 _gitignore 要變 .gitignore 文件,防止被忽略 for (const rawPath of _files) { const targetPath = rawPath.split('/').map(filename => { if (filename.charAt(0) === '_' && filename.charAt(1) !== '_') { return `.${filename.slice(1)}` } if (filename.charAt(0) === '_' && filename.charAt(1) === '_') { return `${filename.slice(1)}` } return filename }).join('/') const sourcePath = path.resolve(source, rawPath) const content = renderFile(sourcePath, data, ejsOptions) if (Buffer.isBuffer(content) || /[^\s]/.test(content)) { files[targetPath] = content } } }) } }}
          class Generator { constructor (context, { pkg = {}, plugins = [], files = {} }) { this.context = context this.plugins = plugins this.pkg = Object.assign({}, pkg) this.files = files this.fileMiddlewares = [] const cliService = plugins.find(p => p.id === '@vue/cli-service') || {} this.rootOptions = cliService.options || {} } async generate () { await this.initPlugins() await this.resolveFiles() this.files['package.json'] = JSON.stringify(this.pkg, null, 2) + '\n' // 寫入文件系統(tǒng) await writeFileTree(this.context, this.files) } async resolveFiles () { // GeneratorAPI 里面的render方法修改了 fileMiddlewares,最后在這里執(zhí)行了 const files = this.files for (const middleware of this.fileMiddlewares) { await middleware(files, ejs.render) } } async initPlugins () { const rootOptions = this.rootOptions for (const plugin of this.plugins) { const { id, apply, options } = plugin // 插件generator文件導出的函數(shù)在這里執(zhí)行 const api = new GeneratorAPI(id, this, options, rootOptions) await apply(api, options, rootOptions) } }}
          async function create () { let answers = await inquirer.prompt(prompts); console.log(answers)
          let preset
          if (answers.preset !== '__manual__') { preset = presets[answers.preset] } else { preset = { useConfigFiles: false, plugins: {} } }
          promptCompleteCbs.forEach(cb => cb(answers, preset))
          // preset.plugins['@vue/cli-service'] = Object.assign({ // projectName: name // }, preset) // 暫時用一個我自己寫的cli插件 preset.plugins['cli-plugin-demo'] = {}
          const pkg = { name, version: '0.1.0', private: true, devDependencies: {} } const deps = Object.keys(preset.plugins) deps.forEach(dep => { pkg.devDependencies[dep] = 'latest' })
          await writeFileTree(context, { 'package.json': JSON.stringify(pkg, null, 2) })
          console.log(`?\u{fe0f} Installing CLI plugins. This might take a while...`) await run('npm', ['install'])
          console.log(`?? Invoking generators...`) // [{ id, apply, options }] id 插件的名字, apply 插件generator文件導出的函數(shù),options 是參數(shù) const plugins = await resolvePlugins(preset.plugins, pkg) const generator = new Generator(context, { pkg, plugins, }) await generator.generate()
          console.log(`?? Installing additional dependencies...`) await run('npm', ['install'])
          console.log(`?? Successfully created project ${name}.`)}
          create()// 最后使用node運行



          瀏覽 52
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  九九草色播免费视频观看 | 婷婷综合伊人 | 综合亚洲自拍 | 青青草网站在线观看 | 一区二区三区999 |