vue(9)—— 组件化开发 - webpack(3)

前面两个终于把webpack相关配置解析完了。现在终于进入vue的开发了

  

vue组件化开发预热 

 

前期准备 

 

创建如下项目:

 vue(9)—— 组件化开发 - webpack(3)_第1张图片

 

 app.js:

vue(9)—— 组件化开发 - webpack(3)_第2张图片

 footer.js:

vue(9)—— 组件化开发 - webpack(3)_第3张图片

main.js:
vue(9)—— 组件化开发 - webpack(3)_第4张图片

 

 

 

webpack.config.js:
vue(9)—— 组件化开发 - webpack(3)_第5张图片

 

同样的生成两个webpack配置文件,webpack.dev.config.js,webpack.prod.config.js,配置跟webpack.config.js一模一样

 

package.json:

vue(9)—— 组件化开发 - webpack(3)_第6张图片

 

 

组件化开发终于到了重头戏了

 

webpack引入vue

 

有几种方法导入

 

1.第一种

这个前面文章 vue(8)—— 组件化开发 - webpack(2)   已经用过了,不多说,直接在html文件里导入

vue(9)—— 组件化开发 - webpack(3)_第7张图片

 

 

2.第二种

 

在入口函数里引入:

vue(9)—— 组件化开发 - webpack(3)_第8张图片

 

然后在webpack配置文件里添加一个resolve属性:

vue(9)—— 组件化开发 - webpack(3)_第9张图片

 

注意,使用import导入的方法导入vue,导入的vue并不是完整的vue对象,只提供runtime-only的方式。

 

 

安装loader 

 

其实vue真的做组件化开发的时候,文件后缀名是.vue,并不是.js,所以webpack要识别后缀为vue的组件的话,需要安装loader

 

这里要注意,需要安装vue-loader和vue-template-compiler,前者是识别vue后缀的文件的,后者是识别vue后缀文件里的组件代码的,并且两者版本是配套,必须匹配,

我这安装的是vue-loader14.1.1 和[email protected]版本,当然你也可以安装更高版本的两个匹配的

vue(9)—— 组件化开发 - webpack(3)_第10张图片

 

webpack配置文件里配置 

 

配置上webpack-dev-server,html-webpack-plugin的配置,这里就略过了,还不太会的回过头去看

 

vue(9)—— 组件化开发 - webpack(3)_第11张图片

 

 

package.json配置:

vue(9)—— 组件化开发 - webpack(3)_第12张图片

 

 在项目根目录创建一个src文件,将刚才的app.js,footer.js,main.js移到src文件夹内,并把app.js和footer.js文件重命名后缀为.vue:

 

 vue(9)—— 组件化开发 - webpack(3)_第13张图片

 

代码规范整合

 

vue文件

 

上面的app.js文件修改成app.vue之后,开发规范已经变了,只有如下三个标签作为逻辑代码,之前我们写的代码已经不认识

 








 

vue(9)—— 组件化开发 - webpack(3)_第14张图片

 

 改成正确的代码:

 

vue(9)—— 组件化开发 - webpack(3)_第15张图片

 

export default 抛出一个vue组件,组件的结构为template,数据为data抛出,因为vue组件的data必须是一个函数,所以这里是函数

style样式则和之前用的没什么区别

 

footer.vue文件:

vue(9)—— 组件化开发 - webpack(3)_第16张图片

 

入口文件 

 

入口函数不用改为js文件

 

 main.js:

vue(9)—— 组件化开发 - webpack(3)_第17张图片

 

 

 

其他配置

 

其他webpack.dev.config.js和package.json不用变

 

 

编译运行 

npm  run dev: 

发现报错了,这里就解释前面文章 vue(7)—— 组件化开发 — webpack(1)   的问题为什么已经在全局安装webpack,在开发环境下还要再装一次了

vue(9)—— 组件化开发 - webpack(3)_第18张图片

 

npm i webpack -D 之后,再次编译运行,注意安装指定版本的webpack vue(7)—— 组件化开发 — webpack(1)

vue(9)—— 组件化开发 - webpack(3)_第19张图片

 

 运行是运行了,但是页面打开报错了,大概意思是说vue-loader和vue-template-compiler这两个插件有问题

vue(9)—— 组件化开发 - webpack(3)_第20张图片

 

按照我多年解决bug的问题,我把当前的vue版本卸载了,然后装了个低版本的,装了个与vue-template-compiler的版本一样的2.5.17版本的,完美解决上面这个问题

 

重新编译:

vue(9)—— 组件化开发 - webpack(3)_第21张图片

 

 发现还是报错,意思是这个App组件没有正确注册,检查代码,发现根本没有问题,把App删除之后,只留一个Footer看看:

 

vue(9)—— 组件化开发 - webpack(3)_第22张图片

 

 可以渲染,但是没有任何数据,按理说是有的

vue(9)—— 组件化开发 - webpack(3)_第23张图片

 

这下怎么办呢?进入关键地步,仔细看

 

初学者容易入的坑

 

错误分析

 

其实按道理完全没有问题,因为在之前非webpack下的vue开发中,在一个html文件就是这么用的没有错,那为什么这里的footer可以,app不行呢?大众思维,先把App删除掉,看Footer呢?

vue(9)—— 组件化开发 - webpack(3)_第24张图片

 

这到底怎么回事呢?标签可以出,但是没有数据渲染,卡在这了。

 

这里如果你去发现研究的话花些时间也会发现问题的,但是为了不浪费你的时间,你不用自己去研究了。

 

其实上面的Footer和App组件都错的,都不可以渲染成功的,其实footer的显示,其实是被浏览器当html5的标签处理了

 

 

vue(9)—— 组件化开发 - webpack(3)_第25张图片

 

如果你运气好,定义的两个组件的名字刚好和html5的标签撞上,那绝对不会报错,但是一样不会把数据渲染出来

 

总之,按以上以前简单的html页面的vue配置,根本就是错的,组件化开发根本不认这种写法。

为什么,就因为多定义了一个局部,然后再从这个局部组件挂载到vue实例,所以关键就在于我标注出的位置

 vue(9)—— 组件化开发 - webpack(3)_第26张图片

 

也就是说,在webpack里,或者说在vue-loader和vue-template-compiler插件里,不认这种写法 

 

 既然都已经是组件化开发了,那么你要定义局部组件,完全可以再新建一个vue文件,然后写上组件代码,最后在入口文件main.js里挂载就行了

 

不信的话,看我现在不通过Main组件挂载,直接在vue实例对象里挂载App和Footer看看:

 

vue(9)—— 组件化开发 - webpack(3)_第27张图片

 

其他配置不变

 

打开页面,是不是显示了,而且没报错

vue(9)—— 组件化开发 - webpack(3)_第28张图片

 

正确引入vue组件

 

 好,再把刚才按个Main组件新建一个vue文件放进去

 vue(9)—— 组件化开发 - webpack(3)_第29张图片

 

注意在vue文件里导入另一个vue文件的用法

 

main.js:

vue(9)—— 组件化开发 - webpack(3)_第30张图片

 

其他不变,打开网页,正确返回

vue(9)—— 组件化开发 - webpack(3)_第31张图片

 

 

好了组件化开发规范终于解析完了,下面才开始真正的开发测试

 

Vue组件化开发

 

掌握以上的规范之后,你就可以利用前面学到的vue开发,只要符合规范,随心所欲的在里面写你的代码了。如下,给一个简单的例子:

 

package.json:

vue(9)—— 组件化开发 - webpack(3)_第32张图片

 

 webpack.dev.config.js

 

 vue(9)—— 组件化开发 - webpack(3)_第33张图片

 

入口文件  main.js:

vue(9)—— 组件化开发 - webpack(3)_第34张图片

 

main.vue

 vue(9)—— 组件化开发 - webpack(3)_第35张图片

 

app.vue

 

vue(9)—— 组件化开发 - webpack(3)_第36张图片

 

 footer.vue

vue(9)—— 组件化开发 - webpack(3)_第37张图片

 

index.html

 

vue(9)—— 组件化开发 - webpack(3)_第38张图片

 

 

编译运行

 

打开网页:

vue(9)—— 组件化开发 - webpack(3)_第39张图片

 

 

页面数据是有了,但是这个有个问题,css样式乱了,我明明每个vue组件都设置了css的,但是都乱了

 

问题解决

 

这个就不多说了,在每个vue组件的sytle标签里都加上  scoped参数就行了

vue(9)—— 组件化开发 - webpack(3)_第40张图片

 

这个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"
  }
}
package.json
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`  //参照物
        })
    ]
}
webpack.dev.config.js
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`  //参照物
        })
    ]
}
webpack.prod.config.js
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.js


Main.vue


app.vue


footer.vue



    
    


    
    

test

index.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:

vue(9)—— 组件化开发 - webpack(3)_第41张图片

 

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:

vue(9)—— 组件化开发 - webpack(3)_第42张图片

 

其他不变,直接用$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,不需要你再自己定义一个方法了,大家都公用的

vue(9)—— 组件化开发 - webpack(3)_第43张图片

 

 

其他没做任何改动,npm run dev运行,打开浏览器:这个不太好展示效果,效果就是我点增加或减少按钮,两处的数据都跟着改变

vue(9)—— 组件化开发 - webpack(3)_第44张图片

感觉还是挺简单的对吧

 

 

相关代码:

{
  "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"
  }
}
package.json
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.js


main.vue


app.vue


footer.vue



    
    


    
    

test

index.html

 

 

actions和同步异步问题

 

在vuex的官网中,我们可以看到,在mutations中的方法都是同步更新数据的,在actions中的操作时异步更新数据的。如果我们的数据需要异步操作的话,就不能实现实时更新数据了,这样就很容易有后患,比如,组件A将store里的数据用异步操作改了,然后另一个组件B获取数据时因为异步操作获取数据并不及时,所以可能导致获取的数据并不是最新的,所以会有这种隐患存在。

 

看个例子:

为上面的例子加一个异步增加的方法:

main.js,用setTimeout模拟异步操作

vue(9)—— 组件化开发 - webpack(3)_第45张图片

 

 

 

app.vue:

vue(9)—— 组件化开发 - webpack(3)_第46张图片

 

编译运行

 

我先点同步增加:

 

vue(9)—— 组件化开发 - webpack(3)_第47张图片

 

这里查看数据的工具,使用的是谷歌浏览器的vue-devtools插件,这个需要自己手动安装,然后就可以在调试工具窗口中看到vue的相关数据了,这个方便以后的开发,具体就不掩饰怎么安装了,自行网上查询解决

 

再点异步增加:

vue(9)—— 组件化开发 - webpack(3)_第48张图片

 

发现这个问题了吧?虽然在当前页面会显示正常的,但是实际的仓库中的数据其实还没有变化的。

再点减少按钮模拟其他的组件要操作这个数据,一步错,步步错,后面的操作就永远不是正确的数据了:

vue(9)—— 组件化开发 - webpack(3)_第49张图片

 

  

问题解决

 

前面已经说了,mutations里的方法是同步的,然后模拟异步操作时,因为mutations获取操作的数据是同步并不会拿到最新的数据,所以在这种场景下,不用使用直接mutations里的方法来操作数据,需要借用actions来操作:

 

在main.js添加actions:

vue(9)—— 组件化开发 - webpack(3)_第50张图片

 

 app.vue中,注意异步操作分发用的不是commit而是dispatch了

 vue(9)—— 组件化开发 - webpack(3)_第51张图片

 

编译运行

果然同步了,目前这个才是我们希望的效果:

vue(9)—— 组件化开发 - webpack(3)_第52张图片

 

actions函数和传参

在actions的方法中,方法名就是mutations中的方法名,这样以遍actions分发到mutations中的同名方法中 ,格式  XX({commit}){}

如果要传参的话,格式   xx({commit},value){}  ,其中value就是传入的参数,比如:

vue(9)—— 组件化开发 - webpack(3)_第53张图片

 

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"
  }
}
package.json
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.js


main.vue


app.vue


footer.vue



    
    


    
    

test

index.html

 

 

知识点补充

 

render渲染

在vue中,渲染网页数据还可以用render渲染:

 

其他配置如上,只是入口函数Vue实例对象部分,将template属性注释掉,使用了render渲染,里面传入的createdElements是一个固定的方法,它能把传入的组件模板渲染成html结构,个人觉得render跟template的用法是一样的都会把之前的模板清理掉重新渲染使用就不会清理掉再重新渲染

 

不过这个与templated的相同与否还有待各项测试论证,只是我个人发现

 

vue(9)—— 组件化开发 - webpack(3)_第54张图片

 

如果你实在记不住createdElements方法,render还有更简单的写法,用箭头函数:

 

 

 

打开网页: 

vue(9)—— 组件化开发 - webpack(3)_第55张图片

 

 

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(9)—— 组件化开发 - webpack(3)_第56张图片

 

拉取vue-cli旧版本

 

因为vue-cli就跟Python的版本2和版本3一样,有很大不同,但也有很多相同,所以最好还是两个版本都装上最好:

npm i @vue/cli-init -g

vue(9)—— 组件化开发 - webpack(3)_第57张图片

 

vue-cli创建项目须知

 

3.0版本创建是用 vue create:

vue(9)—— 组件化开发 - webpack(3)_第58张图片

2.0版本用 vue init:

 

注:使用不同版本的vue-cli创建项目时,相关的参数也不一样的:

vue(9)—— 组件化开发 - webpack(3)_第59张图片

 

其中vue init中的