上篇介紹了打包和構建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 就可所有打包了