模仿dcloudio/uni-preset-vue 自定义基于uni-app的小程序框架

这篇具有很好参考价值的文章主要介绍了模仿dcloudio/uni-preset-vue 自定义基于uni-app的小程序框架。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

我们知道通过

vue create -p dcloudio/uni-preset-vue my-project

可以创建小程序框架。这个是DCloud 基于vue-cli 来实现的uni-app框架。使用过的都知道,有好几个模板进行选择。如果我们有自己的项目框架,如何做成预设模板,一行命令一键生成项目框架呢?例如提供给其他团队项目框架,只需要告诉一下命令就可以了,方便不少。

这个功能是通过vue-cli  create 命令, -p 参数实现的,下面我们先分析一下整个流程。

1. vue create -p dcloudio/uni-preset-vue my-project 流程

要分析清楚这个流程,肯定需要调试,分享一个笨方法。局部安装vue-cli 然后在node-module/@vue/cli目录下添加log 即可。例如:新建vue-cli-local 目录 在此目录下 执行

npm install @vue/cli

局部安装vue-cli 。我们用vscode 加载vue-cli-local 目录 在node-module/@vue/cli/bin/vue.js文件中添加一行打印:

program
  .command('create <app-name>')
  .description('create a new project powered by vue-cli-service')
  .option('-p, --preset <presetName>', 'Skip prompts and use saved or remote preset')
  .option('-d, --default', 'Skip prompts and use default preset')
  .option('-i, --inlinePreset <json>', 'Skip prompts and use inline JSON string as preset')
  .option('-m, --packageManager <command>', 'Use specified npm client when installing dependencies')
  .option('-r, --registry <url>', 'Use specified npm registry when installing dependencies (only for npm)')
  .option('-g, --git [message]', 'Force git initialization with initial commit message')
  .option('-n, --no-git', 'Skip git initialization')
  .option('-f, --force', 'Overwrite target directory if it exists')
  .option('--merge', 'Merge target directory if it exists')
  .option('-c, --clone', 'Use git clone when fetching remote preset')
  .option('-x, --proxy <proxyUrl>', 'Use specified proxy when creating project')
  .option('-b, --bare', 'Scaffold project without beginner instructions')
  .option('--skipGetStarted', 'Skip displaying "Get started" instructions')
  .action((name, options) => {
    if (minimist(process.argv.slice(3))._.length > 1) {
      console.log(chalk.yellow('\n Info: You provided more than one argument. The first one will be used as the app\'s name, the rest are ignored.'))
    }
    // --git makes commander to default git to true
    if (process.argv.includes('-g') || process.argv.includes('--git')) {
      options.forceGit = true
    }
    console.log("liubbc name: ", name, "; options: ", options)
    return
    require('../lib/create')(name, options)
  })

在vue-cli-local目录下执行如下命令:

node node_modules/@vue/cli/bin/vue.js create -p  dcloudio/uni-preset-vue  my-project

就看到了加的打印:

模仿dcloudio/uni-preset-vue 自定义基于uni-app的小程序框架,前端工程化,vue.js,uni-app,小程序

1.1 处理preset.json

 按此方法继续往下看代码:

  const creator = new Creator(name, targetDir, getPromptModules())   
  await creator.create(options)

  async create (cliOptions = {}, preset = null){
    if (!preset) {
      if (cliOptions.preset) {
        //因为vue-create 命令 加了 -p 参数 所以走这里,进行解析预设模板
        // vue create foo --preset bar
        preset = await this.resolvePreset(cliOptions.preset, cliOptions.clone)
      } else if (cliOptions.default) {
        // vue create foo --default
        preset = defaults.presets['Default (Vue 3)']
      } else if (cliOptions.inlinePreset) {
        // vue create foo --inlinePreset {...}
        try {
          preset = JSON.parse(cliOptions.inlinePreset)
        } catch (e) {
          error(`CLI inline preset is not valid JSON: ${cliOptions.inlinePreset}`)
          exit(1)
        }
      } else {
        preset = await this.promptAndResolvePreset()
      }
    }
    .......
  }

如代码中的注释所示加了vue create 命令加了-p 参数所以去解析预设模板。

解析预设模板代码调用如下:

resolvePreset -> loadRemotePreset -> require('download-git-repo') -> loadPresetFromDir

通过download-git-repo 库去下载远程模板,下载的模板放在一个临时文件夹中。模板必须包含preset.json文件,否则会报错退出。还检查是否包含generator.js 文件,如果包含则为preset的plugins字段再添加两个字段: _isPreset: true,  prompts: true 代码如下:

module.exports = async function loadPresetFromDir (dir) {
  const presetPath = path.join(dir, 'preset.json')
  if (!fs.existsSync(presetPath)) {
    throw new Error('remote / local preset does not contain preset.json!')
  }
  const preset = await fs.readJson(presetPath)

  // if the preset dir contains generator.js or generator/index.js, we will inject it as a hidden
  // plugin so it will be invoked by the generator.
  const hasGenerator = fs.existsSync(path.join(dir, 'generator.js')) || fs.existsSync(path.join(dir, 'generator/index.js'))
  if (hasGenerator) {
    (preset.plugins || (preset.plugins = {}))[dir.replace(/[/]$/, '')] = {
      _isPreset: true,
      prompts: true
    }
  }

  return preset
}

从字面意思看,这两个字段说明这个插件是预设模板,且有提示符供选择。

download-git-repo 支持gitlab github,这次我们预设模板放在gitee上,所以不能像

vue create -p dcloudio/uni-preset-vue my-project  这样使用。但可以使用原始URL的方式,加direct前缀,--clone选项,如下所示:

vue create -p direct:https://gitee.com/liubangbo/mp-d-tab-preset.git  --clone my-project

走到这里,我们就可以新建一个空预设模板放到gitee上,当然要包含preset.json文件,否则会报错。

我们打印看一下 dcloudio/uni-preset-vue 预设:

模仿dcloudio/uni-preset-vue 自定义基于uni-app的小程序框架,前端工程化,vue.js,uni-app,小程序

接着往下走:

    // clone before mutating
    preset = cloneDeep(preset)
    // inject core service
    preset.plugins['@vue/cli-service'] = Object.assign({
      projectName: name
    }, preset)

给preset plugins字段注入@vue/cli-service字段。注意vue-cli 有2大部分,一部分就是cli  另一部分就是cli-service,这里就不详细描述了。

接下来就是生成就是创建项目目录,并生成package.json

    // generate package.json with plugin dependencies
    const pkg = {
      name,
      version: '0.1.0',
      private: true,
      devDependencies: {},
      ...resolvePkg(context)
    }
    const deps = Object.keys(preset.plugins)
    deps.forEach(dep => {
      if (preset.plugins[dep]._isPreset) {
        //在这里把预设中的包含_isPreset的插件给过滤掉了
        return
      }

      let { version } = preset.plugins[dep]

      if (!version) {
        if (isOfficialPlugin(dep) || dep === '@vue/cli-service' || dep === '@vue/babel-preset-env') {
          version = isTestOrDebug ? `latest` : `~${latestMinor}`
        } else {
          version = 'latest'
        }
      }

      pkg.devDependencies[dep] = version
    })    

    // write package.json
    await writeFileTree(context, {
      'package.json': JSON.stringify(pkg, null, 2)
    })

生成的package.json如下:

模仿dcloudio/uni-preset-vue 自定义基于uni-app的小程序框架,前端工程化,vue.js,uni-app,小程序

 后面就是Initializing git repository, install 这些依赖,这样就把cli-service 给安装上了。

走到这里注意一下,下载的预设只处理了preset.json文件,并没有处理generator.js文件。

--------------------------------------------------------------------------------------------------------------------------------

下面就进入复杂的部分,开始处理generator.js了。

1.2 加载generator.js

resolvePlugins处理了generator.js,我们添加了打印,把id, 和options都打印出来看下。

  // { id: options } => [{ id, apply, options }]
  async resolvePlugins (rawPlugins, pkg) {
    // ensure cli-service is invoked first
    rawPlugins = sortObject(rawPlugins, ['@vue/cli-service'], true)
    const plugins = []
    for (const id of Object.keys(rawPlugins)) {      
      const apply = loadModule(`${id}/generator`, this.context) || (() => {})
      let options = rawPlugins[id] || {}
      console.log("liubbc id: ", id, "; options: ", options)
      if (options.prompts) {
        let pluginPrompts = loadModule(`${id}/prompts`, this.context)

        if (pluginPrompts) {
          const prompt = inquirer.createPromptModule()

          if (typeof pluginPrompts === 'function') {
            pluginPrompts = pluginPrompts(pkg, prompt)
          }
          if (typeof pluginPrompts.getPrompts === 'function') {
            pluginPrompts = pluginPrompts.getPrompts(pkg, prompt)
          }

          log()
          log(`${chalk.cyan(options._isPreset ? `Preset options:` : id)}`)
          options = await prompt(pluginPrompts)
        }
      }

      plugins.push({ id, apply, options })
    }
    return plugins
  }

模仿dcloudio/uni-preset-vue 自定义基于uni-app的小程序框架,前端工程化,vue.js,uni-app,小程序

 由于dcloudio/uni-preset-vue 预设 有prompts文件,所以会弹出提示供用户选择。我们一般只有一个项目框架,不需要prompts,这里就不分析了。

注意从代码看这里只是load generator.js 赋值给了plugins,并没有执行generator.js。

1.3 执行generator.js

看下处理过的plugins长什么样:

模仿dcloudio/uni-preset-vue 自定义基于uni-app的小程序框架,前端工程化,vue.js,uni-app,小程序

有3个字段,id, apply, options 。 下面就要分析一下 apply在哪里执行的。

    const generator = new Generator(context, {
      pkg,
      plugins,
      afterInvokeCbs,
      afterAnyInvokeCbs
    })
    
    await generator.generate({
      extractConfigFiles: preset.useConfigFiles
    })

创建Generator实例,并执行generate方法。

  async generate ({
    extractConfigFiles = false,
    checkExisting = false,
    sortPackageJson = true
  } = {}) {
    await this.initPlugins()

    // save the file system before applying plugin for comparison
    const initialFiles = Object.assign({}, this.files)
    // extract configs from package.json into dedicated files.
    this.extractConfigFiles(extractConfigFiles, checkExisting)
    // wait for file resolve
    await this.resolveFiles()
    // set package.json
    if (sortPackageJson) {
      this.sortPkg()
    }
    this.files['package.json'] = JSON.stringify(this.pkg, null, 2) + '\n'
    // write/update file tree to disk
    await writeFileTree(this.context, this.files, initialFiles, this.filesModifyRecord)
  }
  async initPlugins () {
    const { rootOptions, invoking } = this
    const pluginIds = this.plugins.map(p => p.id)

    // avoid modifying the passed afterInvokes, because we want to ignore them from other plugins
    const passedAfterInvokeCbs = this.afterInvokeCbs
    this.afterInvokeCbs = []
    // apply hooks from all plugins to collect 'afterAnyHooks'
    for (const plugin of this.allPlugins) {
      const { id, apply } = plugin
      const api = new GeneratorAPI(id, this, {}, rootOptions)

      if (apply.hooks) {
        await apply.hooks(api, {}, rootOptions, pluginIds)
      }
    }

    // We are doing save/load to make the hook order deterministic
    // save "any" hooks
    const afterAnyInvokeCbsFromPlugins = this.afterAnyInvokeCbs

    // reset hooks
    this.afterInvokeCbs = passedAfterInvokeCbs
    this.afterAnyInvokeCbs = []
    this.postProcessFilesCbs = []

    // apply generators from plugins
    for (const plugin of this.plugins) {
      const { id, apply, options } = plugin
      const api = new GeneratorAPI(id, this, options, rootOptions)      
      await apply(api, options, rootOptions, invoking)

      if (apply.hooks) {
        // while we execute the entire `hooks` function,
        // only the `afterInvoke` hook is respected
        // because `afterAnyHooks` is already determined by the `allPlugins` loop above
        await apply.hooks(api, options, rootOptions, pluginIds)
      }
    }
    // restore "any" hooks
    this.afterAnyInvokeCbs = afterAnyInvokeCbsFromPlugins
  }

最终在initPlugins方法中执行了apply方法,也就是预设模板中generator.js中导出的方法。

走到这里执行generator.js的地方就找到了。分析dcloudio/uni-preset-vue (

GitHub - dcloudio/uni-preset-vue: uni-app preset for vue)可以看出,基本上就是扩展package.json中的内容,以及拷贝项目框架文件。

在这里注意3点:

1)要新建一个node 工程,在package.json中安装用到的node 库 例如,glob等。并且需要把node_modules上传

2)在放项目框架文件的时候要注意,/template/common  里面的文件是和src文件平级的

     src/目录下的文件要放到/template/default/目录下

3).env .ignore文件要修改一下名称 改为 _env  _ignore

我们仿照generator.js 如下所示:

const fs = require('fs')
const path = require('path')

const isBinary = require('isbinaryfile')

async function generate(dir, files, base = '', rootOptions = {}) {  
  const glob = require('glob')
  glob.sync('**/*', {
    cwd: dir,
    nodir: true
  }).forEach(rawPath => {    
    const sourcePath = path.resolve(dir, rawPath)
    const filename = path.join(base, rawPath)    
    if (isBinary.sync(sourcePath)) {
      files[filename] = fs.readFileSync(sourcePath) // return buffer
    } else {
      let content = fs.readFileSync(sourcePath, 'utf-8')
      if (path.basename(filename) === 'manifest.json') {
        content = content.replace('{{name}}', rootOptions.projectName || '')
      }
      if (filename.charAt(0) === '_' && filename.charAt(1) !== '_') {
        files[`.${filename.slice(1)}`] = content
      } else if (filename.charAt(0) === '_' && filename.charAt(1) === '_') {
        files[`${filename.slice(1)}`] = content
      } else {
        files[filename] = content
      }
    }
  })
}

module.exports = (api, options, rootOptions) => {
  api.extendPackage(pkg => {
    return {
      dependencies: {
        'regenerator-runtime': '^0.12.1',// 锁定版本,避免高版本在小程序中出错
        '@dcloudio/uni-helper-json': '*',
        "@dcloudio/uni-ui": "^1.4.19",
        "core-js": "^3.6.5",
        "flyio": "^0.6.2",
        "uview-ui": "^2.0.31",
        "vue": "^2.6.11",
        "vuex": "^3.2.0"
      },
      devDependencies: {
        "@babel/runtime": "~7.17.9",// 临时指定版本,7.13.x 会报错
        'postcss-comment': '^2.0.0',
        '@dcloudio/types': '^3.0.4',
        'miniprogram-api-typings': '*',
        'mini-types': '*',
        "cross-env": "^7.0.2",
        "glob": "^8.0.3",
        "jest": "^25.4.0",
        "open-devtools": "^0.2.1",
        "sass": "^1.53.0",
        "sass-loader": "^10.3.1",
        "vue-template-compiler": "^2.6.11"
      },
      resolutions: {
        "@babel/runtime": "~7.17.9"
      }
    }
  })
  
  api.render(async function (files) {
    Object.keys(files).forEach(name => {
      delete files[name]
    })

    const base = 'src'
    await generate(path.resolve(__dirname, './template/common'), files)
    await generate(path.resolve(__dirname, './template/default'), files, base, rootOptions)    
  })
}

最后在说一下使用方式:

vue create -p direct:https://gitee.com/liubangbo/mp-d-tab-preset.git  --clone my-project

欢迎下载使用。文章来源地址https://www.toymoban.com/news/detail-516914.html

到了这里,关于模仿dcloudio/uni-preset-vue 自定义基于uni-app的小程序框架的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Module build failed (from ./node_modules/@dcloudio/vue-cli-plugin-uni/packages/sass-loader/dist/cjs.

    TypeError: Cannot read property \\\'props\\\' of undefined [Vue warn]: Failed to resolve async component: function (resolve) 遇到以上三种问题,在我的项目中都归结为一个:就是依赖的sass-loader没有,或者说是依赖的node-sass没有 所以解决办法就是 在package.json查看  {   \\\"devDependencies\\\": {     \\\"node-sass\\\": \\\"^9.0.0\\\",

    2024年02月09日
    浏览(56)
  • Error: Cannot find module ‘@dcloudio/uni-i18n‘

    用uni-app开发微信小程序,拉取项目后,使用 npm install 后,在微信开发者工具中一直报下面这句错 反复安装了很多遍@dcloudio,@dcloudio/uni-i18n都不行,最后解决方法是,找到项目中 uview-ui文件下的package.json ,文件中有个 sass-loader ,把那一句的删掉 微信开发者工具就不报错了,

    2024年02月14日
    浏览(66)
  • 前端vue uni-app自定义精美海报生成组件

    在当前技术飞速发展的时代,软件开发的复杂度也在不断提高。传统的开发方式往往将一个系统做成整块应用,一个小的改动或者一个小功能的增加都可能引起整体逻辑的修改,从而造成牵一发而动全身的情况。为了解决这个问题,组件化开发逐渐成为了一种趋势。通过组件

    2024年02月14日
    浏览(60)
  • 在uniapp开发编译成小程序时,模板编译错误Module build failed (from ./node_modules/@dcloudio/webpack-uni-mp-loader/lib/

    解决方案:应该是你的ifdef 和 endif不匹配。你自己看看你的代码,是不是有的地方只有一个endif或者只有ifdef,或者说写错了,检查下。我的就是少了endif. 希望我的解决方案能帮到你

    2024年02月16日
    浏览(50)
  • 【vue】vue商城设计-模仿京东商城

    设计结课作业,课程设计无处下手,网页要求的总数量太多?没有合适的模板?数据库,java,python,vue,html作业复杂工程量过大?毕设毫无头绪等等一系列问题。你想要解决的问题,在微信公众号“coding加油站”中全部会得到解决 vue模仿京东商城系统采用vue技术来实现,符合

    2024年02月09日
    浏览(33)
  • uni-app基于vue实现商城小程序

    目录 一、前言 二、功能效果图 1.首页 2.分类 ​3.活动 4.我的 ​5.商品详情 6.购物车 三、代码实现 1.项目结构截图 uni-app,Hbuilder 2.首页源码 3.数据模拟通讯 四、总结 参考“网易严选”小程序 项目采用传统vue项目结构,即uni-app打包和运行成小程序,使用HBuilder开发工具开发项

    2024年02月03日
    浏览(51)
  • 基于webdriver协议用requests模仿selenium

    你在做下面的练习之前应该具备 安装好chrome,并通过chrome://version确认其版本号 安装chromedriver并与你的chrome版本相匹配,下载路径如下 你应该有一个python+ide(如pycharm)的环境,反正下面的示例是用这些来完成的。 下好requests库,对语法不再阐述 本质上是个web server 在命令行

    2024年02月04日
    浏览(36)
  • 前端vue uni-app基于原生input组件的增强简单通用实用输入框

    随着技术的发展,开发的复杂度也越来越高,传统开发方式将一个系统做成了整块应用,经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改,造成牵一发而动全身。通过组件化开发,可以有效实现单独开发,单独维护,而且他们之间可以随

    2024年02月04日
    浏览(45)
  • 基于ThinkPHP6.0+Vue+uni-app的多商户商城系统好用吗?

    likeshop多商户商城系统适用于B2B2C、多商户、商家入驻、平台商城场景。完美契合平台+自营+联营+加盟等多种经营方式使用,系统拥有丰富的营销玩法,强大的分销能力,支持官方旗舰店,商家入驻,平台抽佣+商家独立结算,统一下单+订单拆分。无论是运营还是二开都是性价

    2024年02月07日
    浏览(50)
  • golang: 模仿 VictoriaMetrics 中的做法,通过把局部变量放在自定义 Context 对象中来做到hot path 的 0 alloc

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 使用 benchmark 压测过程中通常会出现这样的信息: 可以看见 f1 在每次运行都产生了 28 次内存分配。 gc 通常是 golang 最大的性能杀手,减少内存分配对性能提升非常明显

    2024年02月17日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包