一、我为什么不使用vue-cli脚手架?
众所周知,vue相对来说,比较容易上手,搭建一个工程,一行npm install vue-cli -g
能帮我们快速搭建一个项目架构,我们不用去想webpack怎么设计,vue-loader是做什么用的等等,因为vue-cli已经帮助我们搭建了最基本的开发环境,我们装好了cli,就可以直接进入代码的开发阶段。
当然,单纯的使用cli,也能开发出优秀的项目,但是对于后续项目的多样化,一些优秀的插件的不断更新,使我们不能更好的迭代和更新我们的项目,对vue的理解和项目统筹的能力都没有更大的提高,比如现在让你webpack2升级到webpack4,你会不会一脸懵逼?
所以在看了一些文字,和对公司项目的理解下,搭建了一套vue+webpack的框架,既满足SPA的开发,也能够满足一些构建单独网页的多入口项目框架,当然这只满基本需要的1.0版本,更多的润色与拆分,留着各位自行扩展吧。
二、初级构建
新建一个some文件夹,打开终端开始构建
npm init
安装框架所需基本:
npm i webpack vue vue-loader -D
(vue-loader:解析和转换 .vue 文件,提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模版 template,再分别把它们交给对应的 Loader 去处理)
npm i webpack-cli -s -d
(webpack4 已经开始使用webpack-cli)
npm i css-loader vue-template-compiler -D
(css-loader:加载由 vue-loader 提取出的 CSS 代码。 )
(vue-template-compiler:把 vue-loader 提取出的 HTML 模版编译成对应的可执行的 JavaScript 代码)
然后我们在src目录下面新建一个app.vue文件,里面就可以写一些关于项目的业务代码:
<template>
<div> luckfine </div>
</template>
<script>
export default {
data () {
//text: 'luckfine'
}
}
</script>
<style>
</style>
后缀为.vue 文件是不可以在浏览器里直接运行的,我们得让它运行起来。
现在我们要在项目根目录下新建一个webpack.config.js文件,webpack(号称打包一切)它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。
我们在src目录下新建一个index.js作为入口文件,顺便在里面写点东西:
// index.js
import Vue from 'vue'
import App from './app.vue'
const root = document.createElement('div')
document.body.appendChild(root)
new Vue({
render: (h) => h(App)
}).$mount(root)
index.js准备完毕之后,那么在webpack.config.js里面就可以这样写:
// webpack.config.js
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
entry: path.join(__dirname, 'src/index.js'),
mode:'develop',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
module: {
rules: [
{
test: /.vue$/,
loader: 'vue-loader'
}
]
},
plugins:[
new VueLoaderPlugin()
],
}
webpack把所有的文件打包成一个bundle.js文件,并且是能在浏览器里面直接运行的代码。现在我们可以在package.json 文件里的scripts对象里面添加一个脚本:
(注意:webpack4中已经在启动时用mode指定启动环境)
"scripts": {
"build": "webpack --config webpack.config.js --mode=development"
},
添加完这段之后,我们再去terminal执行下npm run build
会发现build成功
三,深入扩展(SPA)
现在,我们已经能够用浏览器解析vue文件,但是搭建一个项目框架岂是这么简单的?现在开始我们的spa构建之路。
删掉原来的app.vue 和 index.js
在src 新建一个components,用作我们存放vue文件
在src 新建一个文件夹h5,用作项目一(SPA),里面的pages里面放的就是我们的入口文件的js
因为入口文件已经更改,此时需要更改webpack.config.js
entry: path.join(__dirname, 'src/h5/pages/demo.js'),
为了本地能启动我们的项目,我们可以安装一个devServernpm install webpack-dev-server -s -d
,和html-webpack-pluginnpm install html-webpack-plugin -d -s
此时的webpack.config.js
// webpack.config.js
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: path.join(__dirname, 'src/h5/pages/demo.js'),
mode:'develop',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
module: {
rules: [
{
test: /.vue$/,
loader: 'vue-loader'
}
]
},
resolve: {
extensions: [
'.vue', '.js'
],
modules: ["node_modules"],
alias: {
vue: 'vue/dist/vue.min.js',
components: path.resolve(__dirname + '/src/components/'),
'@': path.resolve('src')
}
},
plugins:[
new HtmlWebpackPlugin(),
new VueLoaderPlugin()
],
devServer: {
historyApiFallback: {
index: `/dist/h5/index.html`
},
host: '0.0.0.0',
disableHostCheck: true
}
}
package.json中添加启动入口"dev": "webpack-dev-server --inline --hot --mode=development",
此时我们在terminal执行npm run dev
即可看到他自动给我们创建了一个index.html,把打包好的js插入进里面,且项目已经可以运行
为了丰富我们的项目,我们还需要项目中引入vuexnpm install vuex -s -d
在src目录下新建一个stores文件夹,里面放入一个getdata.js作为我们第一个获取数据的store
// getdata.js
/*
* 同步形式的store,设置好state中的值即可
* namespaced为true,是为了避免store的module之间,getters、mutations、actions发生命名冲突
*/
import $ from 'jquery'
export default {
namespaced: true,
state: {
// 初始化时,务必要把所有的数据成员做初始化,否则后面数据的更新,将不会触发显示的更新
dataList: []
},
mutations: {
setData(state, res) {
state.dataList = res;
}
},
// 浏览器环境才可以使用actions来获取数据,服务端应该用Node.js的方式获取数据后,通过mutations同步的把数据存入到store
actions: {
getList({
commit,
rootState,
state
}, options) {
return $.ajax({
url: 'http://*******/getLastNotice'+‘公司接口,保密’,
dataType: 'json'
}).then(data => {
commit('setData', data)
})
}
}
}
在我们的入口文件中引入vuex,并把getdata作为我们一个stoe
// demo.js
import Vue from 'vue'
import Vuex from 'vuex'
import App from 'components/demo/demo'
import data from 'stores/getdata'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
data
}
})
const root = document.createElement('div')
document.body.appendChild(root)
new Vue({
render: (h) => h(App),
store
}).$mount(root)
那么此时,我们就可以在组件里请求和获取这个store里面的数据
<style>
</style>
<template>
<div id="test">{{vueData}}</div>
</template>
<script>
import {
mapState
} from 'vuex'
export default {
data () {
// text: 'demo'
},
computed:mapState({
vueData: state => state.data.dataList
}),
mounted () {
this.$store.dispatch('data/getList')
}
}
</script>
此时npm run dev
运行可以看到在组件内,我们请求了这个接口,并且把这个接口数据渲染到了页面上
继续丰富,引入vue-routernpm install vue-router -d -s
为了简便,我就不创建那么多组件了,小伙伴可以自行优化
// demo.js
import Vue from 'vue'
import Vuex from 'vuex'
import VueRouter from 'vue-router'
import App from 'components/demo/demo'
import data from 'stores/getdata'
Vue.use(Vuex)
Vue.use(VueRouter)
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes // short for `routes: routes`
})
const store = new Vuex.Store({
modules: {
data
}
})
const root = document.createElement('div')
document.body.appendChild(root)
new Vue({
render: (h) => h(App),
store,
router
}).$mount(root)
在组件中应用路由
<style>
</style>
<template>
<div>
<div id="test">{{vueData}}</div>
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
<router-view></router-view>
</div>
</template>
<script>
import {
mapState
} from 'vuex'
export default {
data () {
// text: 'demo'
},
computed:mapState({
vueData: state => state.data.dataList
}),
mounted () {
this.$store.dispatch('data/getList')
}
}
</script>
那么此时npm run dev
,可以看到路由也被渲染出来了
四、深入扩展(多入口项目)
好了,到此刻为止,一个单页应用的vue架构就ok了,这时候,假如我们要做一个简单的宣传页,那么我们新建一个路由,然后请求的时候把整个spa都 加载一遍吗?这样是不是太浪费资源了,那么我们在这个框架里,再新建一个文件夹,用来存放单入口文件,这样每次页面加载只这个宣传页的js,这样岂不是美哉。
那么我们知道,vue+webpack的项目基本都是基于node的,我们可以进入的项目,通过在启动时加入参数env='项目名称,入口文件名称',来获取我们将要启动的入口文件的js
小知识点,node有一个process对象,process是一个全局对象,在任何地方都能访问到它,通过这个对象提供的属性和方法,使我们可以对当前运行的程序的进程进行访问和控制,我们就通过process.argv来获取参数
在src下新建一个项目文件夹more-entry
,在more-entry
下建一个pages文件夹用于存放入口文件js,我们新建一个a.js 和 b.js ,
// a.js
import Vue from 'vue'
import Vuex from 'vuex'
import App from 'components/more-entry/a'
import data from 'stores/getdata'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
data
}
})
const root = document.createElement('div')
document.body.appendChild(root)
new Vue({
render: (h) => h(App),
store
}).$mount(root)
// b.js
import Vue from 'vue'
import Vuex from 'vuex'
import App from 'components/more-entry/b'
import data from 'stores/getdata'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
data
}
})
const root = document.createElement('div')
document.body.appendChild(root)
new Vue({
render: (h) => h(App),
store
}).$mount(root)
那么对应的组件为
// a.vue
<style>
</style>
<template>
<div>
<h3>a组件</h3>
<div id="test">{{vueData}}</div>
</div>
</template>
<script>
import {
mapState
} from 'vuex'
export default {
data () {
// text: 'demo'
},
computed:mapState({
vueData: state => state.data.dataList
}),
mounted () {
this.$store.dispatch('data/getList')
}
}
</script>
// b.vue
<style>
</style>
<template>
<div>
<h3>b组件</h3>
<div id="test">{{vueData}}</div>
</div>
</template>
<script>
import {
mapState
} from 'vuex'
export default {
data () {
// text: 'demo'
},
computed:mapState({
vueData: state => state.data.dataList
}),
mounted () {
this.$store.dispatch('data/getList')
}
}
</script>
结合我们的spa项目,此时的webpack.config.js
// webpack.config.js
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const fs = require('fs');
let featureName = 'h5'; // 要启动的项目名称
let page = null; // 启动项目的入口文件js名称
process.argv.some(function(arg) {
console.log(arg)
let arr = arg.match(/\-\-env=([a-zA-Z0-9\-_,]+)/);
if (arr) {
let config = arr[1].split(',');
featureName = config[0];
if (config[1]) {
page = config[1];
}
}
});
module.exports = {
entry: path.join(__dirname, 'src/'+featureName+'/pages/'+page+'.js'),
mode:'develop',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist'),
filename:featureName + '/[name].js'
},
module: {
rules: [
{
test: /.vue$/,
loader: 'vue-loader'
}
]
},
resolve: {
extensions: [
'.vue', '.js'
],
modules: ["node_modules"],
alias: {
vue: 'vue/dist/vue.min.js',
stores: __dirname + '/src/stores/',
components: path.resolve(__dirname + '/src/components/'),
'@': path.resolve('src')
}
},
plugins:[
new HtmlWebpackPlugin(),
new VueLoaderPlugin()
],
devServer: {
// historyApiFallback: {
// index: `/dist/${featureName}/index.html`
// },
host: '0.0.0.0',
disableHostCheck: true
}
}
启动项目npm run dev -- --env=more-entry,b
,那么可以看到浏览器中已经可以渲染出来
npm run dev -- --env=more-entry,a
npm run build -- --env=more-entry,b
可用来单独打包
到此为止,简单的项目架构就出来了,后续我们增加项目,只需要在src下建立对应的文件夹即可,增加常用工具js,引入渲染模板,增加对应loader,引入es-lint,解析es6的语法等等,各位可自行构建。
本文代码https://github.com/luckFine/vue-project.git
码字不易,欢迎打赏。