1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        vue-cli 基本原理

        共 7769字,需瀏覽 16分鐘

         ·

        2021-08-31 09:04


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


        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
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美午夜一区二区三区免费大片 | 女人高潮一级片 | 影音先锋成人资源 | 一区二区视频 | 欧美午夜在线观看 | 波多野毛片 | 女性做爰小视频 | 日产无人区视频 | 娇妻和猛男老外玩3p | 日韩屄视频 |