vue(9)—— 组件化开发 - webpack(3)
前面两个终于把webpack相关配置解析完了。现在终于进入vue的开发了
vue组件化开发预热
前期准备
创建如下项目:
app.js:
footer.js:
main.js:
webpack.config.js:
同样的生成两个webpack配置文件,webpack.dev.config.js,webpack.prod.config.js,配置跟webpack.config.js一模一样
package.json:
组件化开发终于到了重头戏了
webpack引入vue
有几种方法导入
1.第一种
这个前面文章 vue(8)—— 组件化开发 - webpack(2) 已经用过了,不多说,直接在html文件里导入
2.第二种
在入口函数里引入:
然后在webpack配置文件里添加一个resolve属性:
注意,使用import导入的方法导入vue,导入的vue并不是完整的vue对象,只提供runtime-only的方式。
安装loader
其实vue真的做组件化开发的时候,文件后缀名是.vue,并不是.js,所以webpack要识别后缀为vue的组件的话,需要安装loader
这里要注意,需要安装vue-loader和vue-template-compiler,前者是识别vue后缀的文件的,后者是识别vue后缀文件里的组件代码的,并且两者版本是配套,必须匹配,
我这安装的是vue-loader14.1.1 和vue-template-compiler@2.5.17版本,当然你也可以安装更高版本的两个匹配的
webpack配置文件里配置
配置上webpack-dev-server,html-webpack-plugin的配置,这里就略过了,还不太会的回过头去看
package.json配置:
在项目根目录创建一个src文件,将刚才的app.js,footer.js,main.js移到src文件夹内,并把app.js和footer.js文件重命名后缀为.vue:
代码规范整合
vue文件
上面的app.js文件修改成app.vue之后,开发规范已经变了,只有如下三个标签作为逻辑代码,之前我们写的代码已经不认识
<!--结构--> <template> </template> <!--逻辑--> <script> export default { } </script> <!--样式--> <style> </style>
改成正确的代码:
export default 抛出一个vue组件,组件的结构为template,数据为data抛出,因为vue组件的data必须是一个函数,所以这里是函数。
style样式则和之前用的没什么区别
footer.vue文件:
入口文件
入口函数不用改为js文件
main.js:
其他配置
其他webpack.dev.config.js和package.json不用变
编译运行
npm run dev:
发现报错了,这里就解释前面文章 vue(7)—— 组件化开发 — webpack(1) 的问题为什么已经在全局安装webpack,在开发环境下还要再装一次了
npm i webpack -D 之后,再次编译运行,注意安装指定版本的webpack vue(7)—— 组件化开发 — webpack(1)
运行是运行了,但是页面打开报错了,大概意思是说vue-loader和vue-template-compiler这两个插件有问题
按照我多年解决bug的问题,我把当前的vue版本卸载了,然后装了个低版本的,装了个与vue-template-compiler的版本一样的2.5.17版本的,完美解决上面这个问题
重新编译:
发现还是报错,意思是这个App组件没有正确注册,检查代码,发现根本没有问题,把App删除之后,只留一个Footer看看:
可以渲染,但是没有任何数据,按理说是有的
这下怎么办呢?进入关键地步,仔细看
初学者容易入的坑
错误分析
其实按道理完全没有问题,因为在之前非webpack下的vue开发中,在一个html文件就是这么用的没有错,那为什么这里的footer可以,app不行呢?大众思维,先把App删除掉,看Footer呢?
这到底怎么回事呢?标签可以出,但是没有数据渲染,卡在这了。
这里如果你去发现研究的话花些时间也会发现问题的,但是为了不浪费你的时间,你不用自己去研究了。
其实上面的Footer和App组件都错的,都不可以渲染成功的,其实footer的显示,其实是被浏览器当html5的标签处理了
如果你运气好,定义的两个组件的名字刚好和html5的标签撞上,那绝对不会报错,但是一样不会把数据渲染出来
总之,按以上以前简单的html页面的vue配置,根本就是错的,组件化开发根本不认这种写法。
为什么,就因为多定义了一个局部,然后再从这个局部组件挂载到vue实例,所以关键就在于我标注出的位置
也就是说,在webpack里,或者说在vue-loader和vue-template-compiler插件里,不认这种写法
既然都已经是组件化开发了,那么你要定义局部组件,完全可以再新建一个vue文件,然后写上组件代码,最后在入口文件main.js里挂载就行了。
不信的话,看我现在不通过Main组件挂载,直接在vue实例对象里挂载App和Footer看看:
其他配置不变
打开页面,是不是显示了,而且没报错
正确引入vue组件
好,再把刚才按个Main组件新建一个vue文件放进去
注意在vue文件里导入另一个vue文件的用法
main.js:
其他不变,打开网页,正确返回
好了组件化开发规范终于解析完了,下面才开始真正的开发测试
Vue组件化开发
掌握以上的规范之后,你就可以利用前面学到的vue开发,只要符合规范,随心所欲的在里面写你的代码了。如下,给一个简单的例子:
package.json:
webpack.dev.config.js
入口文件 main.js:
main.vue
app.vue
footer.vue
index.html
编译运行
打开网页:
页面数据是有了,但是这个有个问题,css样式乱了,我明明每个vue组件都设置了css的,但是都乱了
问题解决
这个就不多说了,在每个vue组件的sytle标签里都加上 scoped参数就行了
这个scoped就不多说了,它是由css属性选择器实现的,没必要去深究了,反正你就记住设置scoped之后当前的css样式只对当前的元素生效,对其他文件上的html元素不生效就行了
相关代码:
{ "name": "day3", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "webpack-dev-server --open --hot --inline --config ./webpack.dev.config.js", "build": "webpack --config ./webpack.prod.config.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": {}, "devDependencies": { "css-loader": "^2.1.1", "html-webpack-plugin": "^3.2.0", "style-loader": "^0.23.1", "vue": "^2.5.17", "vue-loader": "^14.1.1", "vue-template-compiler": "^2.5.17", "webpack": "^3.12.0", "webpack-dev-server": "^2.9.0" } }
var path = require('path') var htmlwebpackplugin = require('html-webpack-plugin') module.exports = { entry: { name: './src/main.js' }, output: { path: path.resolve('./dist'), // 项目输出文件路径 filename: './bundle.js' }, watch: true, module: { loaders: [ { test: /\.vue$/, loader: 'vue-loader' }, { test: /\.css$/, loader: 'style-loader!css-loader' } ] }, plugins: [ new htmlwebpackplugin({ template: `./index.html` //参照物 }) ] }
var path = require('path') var htmlwebpackplugin = require('html-webpack-plugin') module.exports = { entry: { name: './src/main.js' }, output: { path: path.resolve('./dist'), // 项目输出文件路径 filename: './bundle.js' }, module: { loaders: [ { test: /\.vue$/, loader: 'vue-loader' }, { test: /\.css$/, loader: 'style-loader!css-loader' } ] }, plugins: [ new htmlwebpackplugin({ template: `./index.html` //参照物 }) ] }
import Vue from '../node_modules/vue/dist/vue.js' import Main from './Main.vue' new Vue({ el: '#app', data() { return { } }, components: { Main }, render(createdElements) { return createdElements(Main) }, // template: `<Main />` });
<template> <div class='main'> {{msg}} <App @postHander='postHanders'/> <Footer :cont='sendmsg'/> </div> </template> <script> import App from './app.vue' // 在vue组件里导入另一个vue组件,得在script标签里导入 import Footer from './footer.vue' export default { name:'Main', data(){ return { msg:'Main入口', sendmsg:'', } }, components:{ App, Footer }, methods:{ postHanders(value){ console.log(value,'父级接收到了数据,正准备传给footer组件') this.sendmsg = value // app.vue组件传来的数据,值为true } }, } </script> <style scoped> .main{ color: brown; } </style>
<template> <div class='apps'> {{msg}} <div v-bind:class="isShow"></div> <input type="text" v-model="test"><span>{{test}}</span><br> <input type="text" ref='inputs' v-model='test2'><br> <button @click="btnHander" ref="btn">点我传数据给父级</button> </div> </template> <script> export default { name:'App', data(){ return { msg:'app内容', isShow:true, test:'', test2:'' } }, methods:{ btnHander(){ this.$emit('postHander',this.isShow) this.$refs.btn.innerHTML = '数据已发送' } }, watch: { test2(value){ if(value=='vue'){ this.test2 = '我监听到你填了vue' this.$refs.inputs.value = '' } } } } </script> <style scoped> .apps{ font-size: 12px; background: salmon; color: blue; } </style>
<template> <div class='footers'>{{msg}} <p v-text="test1"></p> <p v-html="test2"></p> <button @click="getHander(cont)">点我展示从父级获取到的数据</button> <ul v-show="isShow"> <li v-for = 'item in datas' :key = 'item.id'> {{ item.name }} -- {{item.age}} </li> </ul> </div> </template> <script> export default { name:'Footer', data(){ return { msg:'footer内容', test1:'测试1', test2:'<h4>测试2</h4>', datas:[ {'id':1,'name':'jack','age':30}, {'id':2,'name':'lucy','age':26}, {'id':3,'name':'lily','age':28},], isShow:false }}, methods:{ getHander(cont){ this.isShow = cont } }, props:['cont'], // 父级传来的数据,值为true mounted(){ // 生命周期函数 console.log() } } </script> <style scoped> .footers{ color: purple; font-size: 15px; font-weight: bold; } </style>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="app"> <p>test</p> </div> </body> </html>
Vuex
什么是vuex
官网:传送门,官方解释:
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能
vuex的作用
vuex是为了保存组件之间的共享数据而诞生的。如果组件之间有共享的数据,直接挂载在vuex上即可,而没必要子级组件通过向父级组件传递之后,父级组件再将数据转给目标子级组件,这个很麻烦,这个之前我个人试过,利用子级传递一个简单的数据还好,传递一个数组感觉有点绕,很不方便。
所以,vuex是一个全局的共享数据存储区域,相当于一个数据仓库
Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择
简单使用vuex
安装 npm i vuex
配置vuex
还是上面的例子,做了稍微修改:
main.js:
1.导入vuex并挂载到Vue实例对象上,这个用法跟挂载vue-router类似,不多说
2.定义一个vuex实例对象,其有三个属性,state,mutations,getters
- state类似vue实例中的data属性,里面的值就是数据仓库,所有的vue组件都可以共用
- mutations类似vue实例中的methods属性,里面的方法类似computed计算属性,里面的方法主要是对数据的修改,方法名自定义,vuex官方不建议直接在数据被引用的组件里直接赋值修改
- getters类似vueshilling中的watch或者filters属性,方法名可以自定义,但是参数固定必须是state属性
3.同样的类似路由对象一样,直接在vue实例上挂载定义的vuex对象
app.vue:
其他不变,直接用$store.state.XX(你定义的公用数据变量名)获取vuex实例的数据
定义了两个方法,与vuex的mutations属性中的方法对应,一个加法,一个减法,传入的第一个值就是mutations里的方法名,第二个值随意,但最多只能传入两个值,当然你可以传入一个对象,对象里放很多属性即可。
注意:组件中的方法要操作store公用仓库的数据,必须使用this.$store.commit或者this.$store.disptch提交,不能是$store.commit,在方法中必须加this,注意区分
footer.vue:
只有标注区域是添加的,其他没变,就是通过了vuex实例的getters属性,获取了最新的数据,有点类似vue的watch或者filters属性,实时获取最新的值
这里可能有疑惑,为什么用getters获取呢?直接像上面的app.vue中的用$store.state.count获取不行吗?可以是可以,假如说你希望给这个数据添加一些其他的样式,加个单位或者什么呢?这样就可以直接用getters,不需要你再自己定义一个方法了,大家都公用的
其他没做任何改动,npm run dev运行,打开浏览器:这个不太好展示效果,效果就是我点增加或减少按钮,两处的数据都跟着改变
感觉还是挺简单的对吧
相关代码:
{ "name": "day3", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "webpack-dev-server --open --hot --inline --config ./webpack.dev.config.js", "build": "webpack --config ./webpack.prod.config.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "vuex": "^3.1.0" }, "devDependencies": { "css-loader": "^2.1.1", "html-webpack-plugin": "^3.2.0", "style-loader": "^0.23.1", "vue": "^2.5.17", "vue-loader": "^14.1.1", "vue-template-compiler": "^2.5.17", "webpack": "^3.12.0", "webpack-dev-server": "^2.9.0" } }
import Vue from '../node_modules/vue/dist/vue.js' import Main from './Main.vue' import Vuex from 'vuex' Vue.use(Vuex) var store = new Vuex.Store({ state: { //类似于data属性 count: 0 }, mutations: { // 类似于methods属性 increment(state) { // 加法 state.count++ }, subtract(state, value) { // 减法 state.count -= value } }, getters: { // 类似于filters过滤器属性 currentCount:function(state) { return '当前最新的count值是:' + state.count } } }) new Vue({ el: '#app', data() { return {} }, components: {Main}, store, template: `<Main />` });
<template> <div class='main'> {{msg}} <App @postHander='postHanders'/> <Footer :cont='sendmsg'/> </div> </template> <script> import App from './app.vue' // 在vue组件里导入另一个vue组件,得在script标签里导入 import Footer from './footer.vue' export default { name:'Main', data(){ return { msg:'Main入口', sendmsg:'', } }, components:{ App, Footer }, methods:{ postHanders(value){ console.log(value,'父级接收到了数据,正准备传给footer组件') this.sendmsg = value // app.vue组件传来的数据,值为true } }, } </script> <style scoped> .main{ color: brown; } </style>
<template> <div class='apps'> {{msg}} <input type='text' v-model="$store.state.count"> <input type='button' value="增加" @click="add"> <input type="button" value="减少" @click="sub"> <div v-bind:class="isShow"></div> <input type="text" v-model="test"><span>{{test}}</span><br> <input type="text" ref='inputs' v-model='test2'><br> <button @click="btnHander" ref="btn">点我传数据给父级</button> </div> </template> <script> export default { name:'App', data(){ return { msg:'app内容', isShow:true, test:'', test2:'' } }, methods:{ add(){ this.$store.commit('increment') }, sub(){ this.$store.commit('subtract',5) }, btnHander(){ this.$emit('postHander',this.isShow) this.$refs.btn.innerHTML = '数据已发送' } }, watch: { test2(value){ if(value=='vue'){ this.test2 = '我监听到你填了vue' this.$refs.inputs.value = '' } } } } </script> <style scoped> .apps{ font-size: 12px; background: salmon; color: blue; } </style>
<template> <div class='footers'>{{msg}} <p v-text="test1"></p> <p v-html="test2"></p> <button @click="getHander(cont)">点我展示从父级获取到的数据</button> <ul v-show="isShow"> <li v-for = 'item in datas' :key = 'item.id'> {{ item.name }} -- {{item.age}} </li> </ul> <h3>{{$store.getters.currentCount}}</h3> </div> </template> <script> export default { name:'Footer', data(){ return { msg:'footer内容', test1:'测试1', test2:'<h4>测试2</h4>', datas:[ {'id':1,'name':'jack','age':30}, {'id':2,'name':'lucy','age':26}, {'id':3,'name':'lily','age':28},], isShow:false }}, methods:{ getHander(cont){ this.isShow = cont } }, props:['cont'], // 父级传来的数据,值为true mounted(){ // 生命周期函数 console.log() } } </script> <style scoped> .footers{ color: purple; font-size: 15px; font-weight: bold; } </style>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="app"> <p>test</p> </div> </body> </html>
actions和同步异步问题
在vuex的官网中,我们可以看到,在mutations中的方法都是同步更新数据的,在actions中的操作时异步更新数据的。如果我们的数据需要异步操作的话,就不能实现实时更新数据了,这样就很容易有后患,比如,组件A将store里的数据用异步操作改了,然后另一个组件B获取数据时因为异步操作获取数据并不及时,所以可能导致获取的数据并不是最新的,所以会有这种隐患存在。
看个例子:
为上面的例子加一个异步增加的方法:
main.js,用setTimeout模拟异步操作
app.vue:
编译运行
我先点同步增加:
这里查看数据的工具,使用的是谷歌浏览器的vue-devtools插件,这个需要自己手动安装,然后就可以在调试工具窗口中看到vue的相关数据了,这个方便以后的开发,具体就不掩饰怎么安装了,自行网上查询解决
再点异步增加:
发现这个问题了吧?虽然在当前页面会显示正常的,但是实际的仓库中的数据其实还没有变化的。
再点减少按钮模拟其他的组件要操作这个数据,一步错,步步错,后面的操作就永远不是正确的数据了:
问题解决
前面已经说了,mutations里的方法是同步的,然后模拟异步操作时,因为mutations获取操作的数据是同步并不会拿到最新的数据,所以在这种场景下,不用使用直接mutations里的方法来操作数据,需要借用actions来操作:
在main.js添加actions:
app.vue中,注意异步操作分发用的不是commit而是dispatch了
编译运行
果然同步了,目前这个才是我们希望的效果:
actions函数和传参
在actions的方法中,方法名就是mutations中的方法名,这样以遍actions分发到mutations中的同名方法中 ,格式 XX({commit}){}
如果要传参的话,格式 xx({commit},value){} ,其中value就是传入的参数,比如:
vuex中的actions的方法本质上还是调用的mutations里的方法,只是做了异步处理分发,在方法本身是没有做改动的。然后再组件部分不再使用commit提交数据,而是用dispatch方法提交,异步操作。
个人感觉这个actions非常类似Python中的装饰器
相关代码:
{ "name": "day3", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "webpack-dev-server --open --hot --inline --config ./webpack.dev.config.js", "build": "webpack --config ./webpack.prod.config.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "vuex": "^3.1.0" }, "devDependencies": { "css-loader": "^2.1.1", "html-webpack-plugin": "^3.2.0", "style-loader": "^0.23.1", "vue": "^2.5.17", "vue-loader": "^14.1.1", "vue-template-compiler": "^2.5.17", "webpack": "^3.12.0", "webpack-dev-server": "^2.9.0" } }
import Vue from '../node_modules/vue/dist/vue.js' import Main from './Main.vue' import Vuex from 'vuex' Vue.use(Vuex) var store = new Vuex.Store({ state: { //类似于data属性 count: 0 }, mutations: { // 类似于methods属性 increment(state) { // 加法 state.count++ }, asyincrement(state) { // 异步加法,两秒之后再加 state.count++ }, subtract(state, value) { // 减法 state.count -= value } }, getters: { // 类似于filters过滤器属性 currentCount:function(state) { return '当前最新的count值是:' + state.count } }, actions:{ // 异步操作 increment({commit}){ //同步加法 commit('increment') }, asyincrement({commit}){ // 异步加法,两秒之后再加 setTimeout(()=>{ commit('asyincrement') },2000) }, } }) new Vue({ el: '#app', data() { return {} }, components: {Main}, store, template: `<Main />` });
<template> <div class='main'> {{msg}} <App @postHander='postHanders'/> <Footer :cont='sendmsg'/> </div> </template> <script> import App from './app.vue' // 在vue组件里导入另一个vue组件,得在script标签里导入 import Footer from './footer.vue' export default { name:'Main', data(){ return { msg:'Main入口', sendmsg:'', } }, components:{ App, Footer }, methods:{ postHanders(value){ console.log(value,'父级接收到了数据,正准备传给footer组件') this.sendmsg = value // app.vue组件传来的数据,值为true } }, } </script> <style scoped> .main{ color: brown; } </style>
<template> <div class='apps'> {{msg}} <input type='text' v-model="$store.state.count"> <input type='button' value="同步增加" @click="add"> <input type='button' value="异步增加" @click="asyadd"> <input type="button" value="减少" @click="sub"> <div v-bind:class="isShow"></div> <input type="text" v-model="test"><span>{{test}}</span><br> <input type="text" ref='inputs' v-model='test2'><br> <button @click="btnHander" ref="btn">点我传数据给父级</button> </div> </template> <script> export default { name:'App', data(){ return { msg:'app内容', isShow:true, test:'', test2:'' } }, methods:{ add(){ //同步增加 //this.$store.commit('increment') this.$store.dispatch('increment') }, asyadd(){ // 异步增加 //this.$store.commit('asyincrement') this.$store.dispatch('asyincrement') }, sub(){ //this.$store.commit('subtract',5) this.$store.dispatch('subtract',5) }, btnHander(){ this.$emit('postHander',this.isShow) this.$refs.btn.innerHTML = '数据已发送' } }, watch: { test2(value){ if(value=='vue'){ this.test2 = '我监听到你填了vue' this.$refs.inputs.value = '' } } } } </script> <style scoped> .apps{ font-size: 12px; background: salmon; color: blue; } </style>
<template> <div class='footers'>{{msg}} <p v-text="test1"></p> <p v-html="test2"></p> <button @click="getHander(cont)">点我展示从父级获取到的数据</button> <ul v-show="isShow"> <li v-for = 'item in datas' :key = 'item.id'> {{ item.name }} -- {{item.age}} </li> </ul> <h3>{{$store.getters.currentCount}}</h3> </div> </template> <script> export default { name:'Footer', data(){ return { msg:'footer内容', test1:'测试1', test2:'<h4>测试2</h4>', datas:[ {'id':1,'name':'jack','age':30}, {'id':2,'name':'lucy','age':26}, {'id':3,'name':'lily','age':28},], isShow:false }}, methods:{ getHander(cont){ this.isShow = cont } }, props:['cont'], // 父级传来的数据,值为true mounted(){ // 生命周期函数 console.log() } } </script> <style scoped> .footers{ color: purple; font-size: 15px; font-weight: bold; } </style>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="app"> <p>test</p> </div> </body> </html>
知识点补充
render渲染
在vue中,渲染网页数据还可以用render渲染:
其他配置如上,只是入口函数Vue实例对象部分,将template属性注释掉,使用了render渲染,里面传入的createdElements是一个固定的方法,它能把传入的组件模板渲染成html结构,个人觉得render跟template的用法是一样的,都会把之前的模板清理掉重新渲染,使用<component is='app id'>就不会清理掉再重新渲染
不过这个与templated的相同与否还有待各项测试论证,只是我个人发现
如果你实在记不住createdElements方法,render还有更简单的写法,用箭头函数:
打开网页:
export语法
export在前面可能说得不太清楚,这里补充说明下
到目前为止,其实看到了两种export,一种是Node.js的webpack中的export,一种是es6语法中的export
- module.exports = {} 是Node中向外抛出变量的形式
- export default {} 是es6中向外抛出变量的形式,与之配套的接收变量导入是用 import {} from './...'
分别的导入导出
- webpack里的module.exports没什么需要注意的,导入方使用 requre('模块名')导入
- es6中的export,当使用export default 向外抛出变量式,import导入接收变量方可以使用任意的变量名接收
- 在一个文件中,export default只能使用一次,只能默认抛出一个默认的变量,但是export default和export (不加default)可以同时使用
- 使用export (不加default)抛出变量时,可以同时抛出多个变量,import导入接收变量一方只能使用 {} 的形式接收,但不必全部接收,需要什么就接收什么,并且必须严格按照export抛出方的变量名接收,但可以用 as 取别名
脚手架式开发
什么是脚手架
最开始的意思就是建筑施工队用的梯子架子等工具,在开发界也有这个词。
这里就不去溯源最开始什么意思,到现在什么意思了,我就直说按我的理解:
脚手架就是说一些已经有人帮你把开发过程要用的工具环境都开发配置好了,你拿来直接可以用就行了,可以帮助你减轻很多重复的工作,更利于让你专注业务开发。这个开发环境就是脚手架
前端开发中的脚手架是一种形象的比喻,比如各类语言的工作环境
Yeoman
关于前端的脚手架官网:传送门 里面的就类似npm官网一样,里面有很多别人写好的脚手架,你拿来直接用就可以了
Yeoman其实是3套工具的总和:
- yo --- 脚手架,自动生成工具
- Grunt、gulp --- 构建工具 (最初只有grunt,后面gulp火了添加进来的)
- Bower、npm --- 包管理工具 (原来是 bower,后面添加了npm)
vue的脚手架
vue的脚手架就是vue-cli,这个cli在前面安装webpack的时候你可能就见过了,因为安装高版本的webpack组件时需要cli支持的。官网:传送门
安装vue-cli
npm i @vue/cli -g
拉取vue-cli旧版本
因为vue-cli就跟Python的版本2和版本3一样,有很大不同,但也有很多相同,所以最好还是两个版本都装上最好:
npm i @vue/cli-init -g
vue-cli创建项目须知
3.0版本创建是用 vue create:
2.0版本用 vue init:
注:使用不同版本的vue-cli创建项目时,相关的参数也不一样的:
其中vue init中的<template>是模板类型的意思
webpack的模板类型有webpack-simple (简单模型)和webpack(复杂模型)
创建简单模型的webpack项目
1. vue init命令创建
注:
创建vue-cli项目时是需要联网的,因为要下载需要的脚手架
创建完成:
里面的参数就不多说了,都应该看得懂吧
2.按照提示,进入webpacksimple-test目录,执行npm install命令:
3.安装开发环境完成之后,直接执行npm run dev:
浏览器自动打开:
很6对吧?这时你发现,卧槽,前面的webpack配置半天的东西,到这里用vue-cli三两句命令就直接出来了,太骚了
用开发工具,vscode打开项目文件夹:
其实跟我们之前的配置真的没多大区别,稍微的有些组件什么的不一样而已,功能完全一样的,就不仔细展开了
4.改数据
你可以直接用它的模板改成你的数据:
也支持热更新,直接就可以用:
具体就不多说了,自己去发现了
创建复杂型webpack项目
1.命令vue init创建
创建时,注意这里要选第一个:
其他相关配置:
之后就是网速限制思想的时候,等待吧
创建完成:
2.进入目录,直接npm run dev:
打开浏览器:
3.改数据
同样的,用开发工具打开项目目录,然后你就可以进行修改了
改完之后你就可以利用npm run build生成你的代码,放到服务器上了,具体怎么改就不掩饰了,根据自己的业务逻辑来了,后面的操作就是前面解析过的vue相关的操作了
导入vue文件问题
这里你可以看webpack模型的源码,npm run dev,映射的是这个文件:
webpack.dev.conf.js导入这个base基本设置文件:
然后注意看webpack.base.conf.js文件的这里:
这个在上面用过的,什么意思呢?反解我们的项目开发目录src,用【@】代替,然后用import导入时,如果导入的是js,vue,json的后缀文件,其后缀可以省去
比如,这是它自动生成的,我什么都还没改,这里的路由js文件,只用就用@代替了前面的绝对路径书写,然后后缀也直接省去了
并且,如果一个文件是index.js,那么在其他文件里导入时可以直接忽略不写,会自动找到index.js文件,比如这是main.js文件,在main.js文件里导入路由文件index.js,直接写成了 import router from './router',效果和 import router from './router/index.js'是一样的
这样更方便我们的开发,节省时间
vue-router取消hash值【#】
在实际开发中,使用vue-router作为开发,因为前面已经说过这个#是干嘛的了,当我们项目上线时 ,可能不想看到这个#,所以可以取消它
所以只需要在router文件的index.js里加个参数把默认的hash改成history模式就行了:
将代码放到git仓库
这个不需多说,怎么配置git,怎么pull,怎么push也不多说,注意的就是善用【.gitignore】文件,只要里面写进去的后缀或者文件夹,在push时就可以忽略不上传
引入UI组件
什么是UI
UI的意思就是用户界面,也就是用户的操作界面,换句话就是展示的页面,UI设计就是界面的样式,到现在,大家都这么理解的UI,bootstrap那种样式用过吧?它就可以说是一个UI组件。
通俗的说,就是各种样式效果,因为光是文字的话,看着是很单调的,这种对于用户体验来说是很不友好的,比如某某网,就不说是谁了,满屏的字,真的是看着很吃力的,而且字还不大,想起当年的我,还在大学里学着非常low的网页设计时,我那个教前端的老师让做一个个人主页出来,说按照他的做,就是满屏的字,图片没几个,而且还是五颜六色的字,真的看着很累。
到现在,已经有很多的UI库出现,比如就大家都爱用的bootstrap,还有font awesome和阿里的iconfont,这些是最常见的,很多平台用的UI都是这几家的。
MUI
官网 :传送门 ,不是小米系统那个MUI哈,这个是个移动端的UI库,据说是移动端的bootstrap,目前很多app用的都是MUI的样式
这个就不多介绍,官网里面基本都有,中文文档什么的写的很详细,还有些demo演示,要用直接看文档就行了。任何项目都可以使用MUI
Vue的UI组件
以下的UI组件原则上只能适用于vue开发,因为都是基于vue.js开发的
Mint UI
Mint UI是基于vue开发的移动端的UI组件,官网:传送门
VUX
VUX也是基于vue和微信的WeUI开发的移动端UI组件,官网:传送门 貌似可以用于开发其他的,不只是vue
Element UI
Element UI是饿了么出的一个基于vue的PC端UI组件,官网:传送门
iView
也是基于vue的一个UI组件,官网:传送门
注:
以上几个UI都是目前比较火的UI组件,因为官网已经介绍的非常详细,所以完全可以按照官网的文档自学。
我有时间再出个文档,没时间就自己按照官网的自学吧
总结
以上就是webpack相关的配置了,vue部分已经完毕,后续原则上不打算更新了,当然如果我觉得有些很经典很有用的点的话,会有机会更新。
准备填我的坑了,前端基础部分和Python部分的坑都还有很多没有填的,后续会把这些更新了,感兴趣的朋友可以关注一波。