自动化生成项目基本模版
当我们在新建项目时,一般是手动新建文件夹,然后定义项目名字,新建入口文件,index.html,.vue文件,新建router store文件等等,这个是每次新建时必不可少的步骤。
其实,初始化项目的时候,新建的内容都差不多,如果我们能用一行指令,帮助我们生成这些模版文件,我们只需要定义个文件名字,可以显著提升开发体验。
新建项目指令
我在package.json里新增了一个指令 init
"init": "node ./src/common/initTemplate/index.js"
当执行这个命令时,会自动去执行,在本地common 目录下新建的js脚本,在module目录下自动生成一个新的文件。
演示
- 输入
npm run init
会提示用户“正在创建项目”
- 要求输入项目名称
- 确认之后
返回用户“该项目已经创建”,我们在src项目下能看到刚刚新建的目录hehehe,有内置的index.vue,App.vue,main.js,router.js
因为考虑到并不是所有开发者都会用到状态管理库,所以基础模版没有引入。
- 执行
npm run dev
之后
就可以在浏览器里看到我们刚刚生成的hehehe 项目了!
- 重复名检测
当新建的项目已有时,会提示用户,“目录已经存在”,要求用户继续输入
代码实现
下图是initTempate 文件结构
实现比较简单
核心代码在 src/common/initTemplate/index.js
template文件夹是我们要生成的项目模版
initTemplate ├── index.js └── template ├── App.vue ├── main.js ├── router.js └── view └── index.vue // 2 directories, 5 files
inquirer
这个库用来询问用户输入项目名称,这是一个比较在处理命令行交互比较常见的库,详情可以查阅文档
前端Node命令行交互工具 —— inquirer
用vue-cli脚手架新建项目的应该都进行过命令交互,vue创建的时候会让你选择vue2还是vue3,也有多选要什么配置,也有输入y或者n选择是否用history路由等,这其实用inquire这个包都能实现。
fs.access
fs.access()方法可以用于测试文件是否存在
流程图
代码
#!/usr/bin/env node console.log('您正在创建项目') const path = require('path') const fs = require('fs') const inquirer = require('inquirer') const stat = fs.stat const targetDir = path.resolve(__dirname, './template') //copy文件目录常用函数,都是常见api, const copyFile = (targetDir, resultDir) => { // 读取文件、目录 fs.readdir(targetDir, function(err, paths) { if (err) { throw err } paths.forEach(function(p) { const target = path.join(targetDir, '/', p) const res = path.join(resultDir, '/', p) let read let write stat(target, function (err, statsDta) { if (err) { throw err } if (statsDta.isFile()) { read = fs.createReadStream(target) write = fs.createWriteStream(res) read.pipe(write) } else if (statsDta.isDirectory()) { fs.mkdir(res, function() { copyFile(target, res) }) } }) }) }) } const question = [ { type: 'input', name: 'name', message: '请输入项目名称:' } ] const createProject = () => { // 询问用户问题 inquirer.prompt(question).then(({ name }) => { // name 为输入的项目名称 name = name.trim() if (!name) { console.log('项目目录不能为空') // 如果输入空,继续询问 createProject() return false } // 目标路径,要放在module目录下 const resultDir = path.resolve(__dirname, '../../module/', name) // fs.access()方法用于测试文件是否存在 fs.access(resultDir, function(err, data) { if (err) { // 创建文件 fs.mkdir(resultDir, function(err, data) { if (err) { throw err } // 复制模版文件 copyFile(targetDir, resultDir) }) console.log(`${name} 项目已创建成功`) } else { console.log(`${name} 项目目录已存在,请输入其他名称`) // 不存在,继续询问 createProject() } }) }).catch(err => { console.log(err) }) } createProject()
Pinia状态管理
文档地址:pinia.web3doc.top/
Pinia /piːnjʌ/ 中文名:皮你啊
Pinia 优势
1.Pinia是一个全新的Vue状态管理库,是Vuex的代替者,尤雨溪强势推荐
翻译翻译 => 官方背书
2.Vue2 和 Vue3 都能支持
翻译翻译 => 既支持options api 又支持compostions api,维护成本低
3.抛弃传统的 Mutation ,只有 state, getter 和 action ,简化状态管理库
翻译翻译 => 少写代码,降低心智负担,再也不用写 mutation
4.不需要嵌套模块,符合 Vue3 的 Composition api,让代码扁平化
翻译翻译 => 多个state,不需要再使用module,现在可以定义多个store,相互之间调用
5.TypeScript支持
翻译翻译 => 没有使用ts,不好解释
6.代码简单,很好的代码自动分割
翻译翻译 => 可以构建多个store,打包管理会自动拆分模块化的设计,便于拆分状态,能很好支持代码分割;
7.极轻, 仅有 1 KB
翻译翻译 => 体积小,不会成为项目的负担
核心概念
- State: 用于存放数据,有点儿类似 data 的概念;
- Getters: 用于获取数据,有点儿类似 computed 的概念;
- Actions: 用于修改数据,有点儿类似 methods 的概念;
- Plugins: Pinia 插件。
Pinia与Vuex代码分割机制
举个例子:某项目有3个store「user、job、pay」,另外有2个路由页面「首页、个人中心页」,首页用到job store,个人中心页用到了user store,分别用Pinia和Vuex对其状态管理。
Vuex的代码分割
打包时,vuex会把3个store合并打包,当首页用到Vuex时,这个包会引入到首页一起打包,最后输出1个js chunk。这样的问题是,其实首页只需要其中1个store,但其他2个无关的store也被打包进来,造成资源浪费。
解决方案:
经常在首页优化时,会考虑到这个场景,一般处理方案是去做vuex的按需加载,beforeCreate 的时候,可以去判断当前页面需要加载哪些store,利用这个api可以实现$store.registerModule
详情可参考文章
pinia的代码分割
打包时,Pinia会检查引用依赖,当首页用到job store,打包只会把用到的store和页面合并输出1个js chunk,其他2个store不耦合在其中。Pinia能做到这点,是因为它的设计就是store分离的,解决了项目的耦合问题。
基本使用
定义state,getters 和vuex基本一样,具体使用可以去官网学
这里仅仅对比修改数据时,两者的区别
明显pinia 的代码量更少,逻辑更清晰,心智负担更小
//store.js import { defineStore } from 'pinia' export const main = defineStore('main', { state: () => { return { configInfo: {} } }, getters: {}, actions: {} }) import * as piniaStore from '../piniaStore' piniaStore.main().$patch({ configInfo: data })
//store.js import { createStore } from 'vuex' export const store = createStore({ state: { count: 0 }, getters: {}, mutations: { increment (state) { state.count++ } }, actions: {}, modules: {} }) export default store //index.vue import { useStore } from 'vuex' const storeVuex = useStore() storeVuex.commit('increment')
总结
总得来说,Pinia 就是 Vuex 的替代版,可以更好的兼容 Vue2,Vue3以及TypeScript。在Vuex的基础上去掉了 Mutation,只保留了 state, getter和action。
Pinia拥有更简洁的语法, 扁平化的代码编排,符合Vue3 的 Composition api