上篇介绍了打包和构建dev服务相关的内容,这篇介绍src相关的构造。html
模块内部 以home为例,vue
采用命令行新建和删除文件,能够保证为保证各个模块之间、各个文件之间的目录结构风格的统一,方便项目的修改与维护。node
在template文件夹下新建init文件夹、router文件夹、views文件夹,分别做为模块初始化、router添加、页面添加的模板文件。webpack
init文件夹下有 main.js,app.vue,router文件夹 store文件夹做为模块的初始文件。git
安装 copy-template-dir 用于复制文件,安装 rimraf用于删除文件,安装inquirer用于获取用户命令行输入。github
npm i copy-template-dir rimraf inquirer -D复制代码
在build文件夹下新建一个init-page.js文件web
建立时,若是模块不存在,测将 template下的init文件夹复制进模块文件夹,而后将tempalte下views文件夹复制进创建的模块下的views文件夹下vue-router
const chalk=require('chalk')
const fs=require('fs')
const copy=require('copy-template-dir')
const rm=require('rimraf')
const pathParam=require('minimist')(process.argv.slice(2))
const args=pathParam.path //传入的参数 如 home/index
const isDelete=pathParam.d==true //是不是 delete-page命令
const {prompt}=require('inquirer')
const path=require('path')
const res=p=>path.join(process.cwd(),p)
//异常处理
let err={msg:''}
Object.defineProperty(err,"msg",{
set(val){
if(val){
console.log(chalk.red(val))
process.exit(1)
}
}})
err.msg=args?'':'please input a router' //验证输入非空
err.msg=/^((\w|-)+)(\/)((\w|-|\/)+)?((\w|-)+)$/.test(args)?'':'input a useful param, not include "*、?、&" and so on' //特殊字符验证
const mod=args.split('/')[0] //模块名
const pat=args.split('/').slice(1).join('/') //路径
fs.stat(res(`src/modules/${mod}/router/${pat}`),(er)=>{ //判断文件是否已存在
if(!er&&!isDelete){
err.msg='file already existed,no create again '
}else if(er&&!isDelete){
createProject()
}else if(isDelete){
deleteProject()
}})
const createProject=()=>{ //新建
const exist=fs.existsSync(res(`src/modules/${mod}`))
if(!exist){
copy(res('template/init'),res(`src/modules/${mod}`),er=>{ //建立模块
err.msg=er?'fail to create module,please checkout':''
console.log('copy module')
processProject() //建立内容
})
}else{
processProject() //建立内容
}}
const processProject=()=>{
prompt([ //查询用户输入
{
type:'input',
message:'please input a title',
name:'title'
}
]).then(r=>{
console.log(r)
copy(res('template/views'),res(`src/modules/${mod}/views/${pat}`),er=>{ //复制页面文件.vue
err.msg=er?'fail to copy views,please checkout':''
console.log('copy views')
})
copy(res('template/router'),res(`src/modules/${mod}/router/${pat}`),{path:pat,module:mod,title:r.title},er=>{ //复制路由文件
err.msg=er?'fail to copy router,please checkout':''
console.log('copy router')
})
const str= fs.readFileSync(res(`src/modules/${mod}/router/index.js`)) //读取路由文件
let str2=str.toString()
let patName=pat.replace(/\//g,'_') //将路由'/'转成'_'
str2=str2.replace(/(Vue.use\(router\))/,`$1\nimport ${patName} from"\.\/${pat}/index.js"`).replace(/(routes:\[)/,`$1\n \.\.\.${patName},`) //替换文件
fs.writeFileSync(res(`src/modules/${mod}/router/index.js`),str2) //写入文件
})
}
const deleteProject=()=>{ //删除
let patName=pat.replace(/\//g,'_')
rm(res(`src/modules/${mod}/views/${pat}`),err=>{ //删除页面文件
console.log('delete view')
})
rm(res(`src/modules/${mod}/router/${pat}`),err=>{ //删除 路由文件
console.log('delete router')
})
const str= fs.readFileSync(res(`src/modules/${mod}/router/index.js`)) //读取主路由文件
let str2=str.toString()
let reg1=new RegExp(`\nimport ${patName} from"\.\/${pat}\/index\.js"`)
let reg2=new RegExp(`\n \.\.\.${patName},`)
str2=str2.replace(reg1,'').replace(reg2,'') //修改主路由文件
console.log(str2)
fs.writeFileSync(res(`src/modules/${mod}/router/index.js`),str2) //写入主路由文件
}复制代码
在package.json scripts中加入vuex
"init-page": "node build/init-page --path",
"delete-page": "node build/init-page -d --path",复制代码
在命令行输入 "npm run init-page 模块名/路径" 、"npm run delete-page 模块名/路径"便可新增、删除文件。express
建立时,以 "npm run init-page home/detail" 命令为例,
copy(res('template/router'),res(`src/modules/${mod}/router/${pat}`),{path:pat,module:mod,title:r.title},er=>{ //复制路由文件
err.msg=er?'fail to copy router,please checkout':''
console.log('copy router')
})复制代码
//template/router/index.js文件:
export default [{
path:'/{{path}}',
component:()=>import('@/modules/{{module}}/views/{{path}}/index.vue'),
meta:{
title:'{{title}}'
}
}]复制代码
删除时 以 “npm run delete-page home/detail”命令为例,先进行输入验证,经过后
项目的基础组件放置 src/component/base文件夹下
在src/common/js文件夹下新建一个registerComponent.js 文件,用于自动注册基础组件
import Vue from "vue"
const files = require.context("@component/base", true, /index\.vue$/) //扫描基础组件目录
files.keys().map(files).map(item => { //生成 require对象数组
const name = item.default.__file.replace(/^(.+\/)((\w|-|_)+)(\/index.vue)$/, "$2").replace(/(\w)/,(v)=>v.toUpperCase()) //获取组件名字并大写首字母
Vue.component(`v${name}`, item.default) //注册组件
})复制代码
在package.json scripts中加入
"dev:mock": "set NODE_ENV='development' && node build/dev.js --open --mock --module",复制代码
在param.js中加入
const param=require('minimist')(process.argv.slice(2))复制代码
mock:param.mock?true:false复制代码
在prod.conf.js文件中加入
new webpack.DefinePlugin({
MOCK:mock,
IS_DEV:true
})复制代码
在项目根目录新建mock文件夹做为mock目录,
项目的请求方法中直接用require获取mock数据
request (o) {
const _this = this.vm
return new Promise((resolve, reject) => {
if (MOCK) { //判断是否启用mock
try {
const data = require(`@mock/${o.url.replace(/\//g, "_")}.js`) //获取mock数据 _this.$load.open() setTimeout(() => { _this.$load.hide() resolve(data) }, 500) } catch (err) { reject({ errCode: 1, msg: "404 wrong request" }) } } else {
....
}catach(err){}
})
},复制代码
在项目生产打包时,并不须要将mock文件打包,需在build/prod.conf.js中设置 uglifyis-webpack-plugin
const uglifyJs=require('uglifyjs-webpack-plugin')复制代码
optimization:{
minimizer:[
new uglifyJs({
exclude:/\/mock/, //忽略mock文件夹的打包
uglifyOptions:{
compress:{
drop_debugger:true,
drop_console:true
}
}
})
],
}复制代码
在 build/dev.js 中 启用 mock静态资源服务器
const {mock,moduleName,openB}=require('./param')
const res=p=>path.join(process.cwd(),p)复制代码
if(mock){
app.use(express.static(res('mock/assets')))
}复制代码
应用node.js子进程进行多个模块的启动和打包
新建build/server.js文件
const {moduleName,mock}=require('./param')
const {exec}=require('child_process')
const port=require('../config/local.js').router //本地存放的端口号
const chalk=require('chalk')
//错误处理
let err={msg:''}
Object.defineProperty(err,'msg',{
set(val){
if(val!==''){
console.log(chalk.red(val))
process.exit()
}
}})
err.msg=moduleName?'':'please input a module name'let line=mock?"set NODE_ENV=development && node build/dev.js --mock --module":"set NODE_ENV=development && node build/dev.js --module"//本地配置的模块名
let modules=Object.keys(port).reduce((ret,item)=>{ //生成配租文件 端口数组
ret.push(item)
return ret
},[])
//遍历并过滤输入的模块
moduleName.split(',').filter(item=>{
let ret= modules.indexOf(item)>-1
if(!ret){ //未配置端口号
console.log(chalk.yellow(`${item} module not found port config;please check in /config/local.js \n`))
}
return ret
}).map(item=>{ // 启动子进程
exec(`${line} ${item}`,(error,stdout,stdErr)=>{
console.log(stdErr)
error && console.log(error)
})
console.log(chalk.green(`${item} running at http://localhost:${port[item]}/${item}.html`))
})复制代码
在package.json scripts加入
"server": "set NODE_ENV='development' && node build/server.js --module",
"server:mock": "set NODE_ENV='development' && node build/server.js --mock --module",复制代码
在命令行 输入 "npm run server home,car" 便可启动 home,car两个模块
获取本地服务端口配置,
const { router } = require(IS_DEV ? "../../../config/local.js" : "../../../config/prod.js")复制代码
页面跳转方法
go (param) {
let { url, data } = param
let r = ""
if (data) {
Object.keys(data).map(item => { //处理页面传参
r += `&${item}=${data[item]}`
})
r = r.slice(1)
}
url = url.replace(/(\.html)/, `$1?${r}`) //注入页面传参
const m = url.split(".")[0]
location.href = window.encodeURI(`http://localhost:${router[m]}/${url}`) },复制代码
如跳转到 home.html/#/index 页
App.go(
{
url:'home.html/#/index',
data:{
id:1
}
}
)复制代码
安装 glob 用于扫描目录文件
npm i glob -D复制代码
在build文件夹下新建一个buildAll.js文件
const glob=require('glob')
const {exec}=require('child_process')
const path=require('path')
const chalk=require('chalk')
glob(path.join(process.cwd(),'/src/modules/*/main.js'),(err,files)=>{ //扫描指定目录下的入口文件
console.log(files)
files.map(item=>{
let name=item.replace(/(\/main.js)$/,'')
let n=name.slice(name.lastIndexOf('/')+1) //解析出模块名
console.log(n)
exec(`set NODE_ENV=production && node build/build.js --module ${n}`,(error,stdout,stdErr)=>{ //调起子进程
console.log(stdout,stdErr)
if(error){
console.log(error)
process.exit()
}
})
})
})复制代码
在package.json scripts中加入
"buildAll": "node build/buildAll.js"复制代码
命令行输入 npm run buildAll 就可所有打包了