Vue.js学习第八天

webpack中的loader、配置vue及插件使用

- webpack中的loader

昨天我们学习了webpack中的css-loader和style-loader,今天我们来学习剩下常用的几个loader

1. less-loader
顾名思义,这个loader模块是用来加载less文件的,安装命令为npm install --save-dev less-loader less 前一个less-loader是用来加载less文件,后一个less是为加载less提供的支持文件,用来转化less成css,安装完成后进行相应的配置即可,配置在webpack中的loader里都有,不过这里我还是贴一下好了

1
2
3
4
5
6
7
8
9
10
{
test: /\.less$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader" // compiles Less to CSS
}]
},

2. url-loader和file-loader
less文件处理完了之后,还有图片文件要处理,这时候轮到我们的url-loader出场了,安装命令为npm install --save-dev url-loader,安装完成后,进入配置,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// 当加载的图片大小小于limit时,他会将图片编译成base64字符串形式 不需要单独的文件来存储
// 当加载的图片大小大于limit时,使用file-loader模块进行加载 会单独打包成另一个文件
// 这个值默认是8k
limit: 8192,
name: 'img/[name][hash:8].[ext]'
}
}
]
},

我们注意到,这里面有一个limit属性,后面跟着的是默认值8192,也就是8k,这里我直接说一下他的作用好了

  1. 当我们的图片文件小于这个limit值,这时候,图片会被编译成base64的字符串形式,不会生成新文件,这时候正常打包文件即可,页面也能正常显示;
  2. 当我们的图片文件大于这个limit值,这时候,我们将文件打包时就会报错,并且要求我们安装file-loader,安装命令为npm install --save-dev file-loader,这个模块不需要另外进行配置,安装完成即可,这个时候我们就可以尝试着重新打包文件,发现并没有报错,但是当我们在浏览器打开它时,发现图片并不能显示,并且控制台显示找不到此文件,文件的路径为网站的根目录,并且在dist目录(存放打包完成后的目录)下,发现生成了一个新的图片文件,文件名为hash类型的,目的是防止重复,所以我们需要在webpack.config.js文件中添加publicPath的一个配置,目的是将路径前加入dist/这个路径,使得其能够找到此图片,具体的配置如下
1
2
3
4
5
6
output: {
path: path.resolve(__dirname,'dist'),
filename: 'bundle.js',
// 涉及到路径 他都会在前面加一个dist 最后就不需要了
publicPath: 'dist/'
},

当然在我们后期进行开发的时候,由于index文件都要统一放置在dist目录下,所以就不存在找不到路径这一说了,到时候删去即可。
这时我们发现文件的名字很乱,这样就会导致我们一时间分不清是什么文件,所以我们还需要在limit下添加一个name属性,如上面的代码所示,这样当图片文件大于limit值时,会自动将生成的新文件在我们的name配置下的路径,并且我们还能指明它的文件名称,这样就方便辨认了。

3.babel-loader
当我们在查看打包完成的bundle.js文件时,我们发现其中还是有ES6的语法,这就意味着在不支持ES6的浏览器中,我们的代码是没有办法运行的,此时就需要我们采用babel-loader的模块帮助我们将ES6的语法转换成ES5。安装命令为:npm install babel-loader@7 babel-core babel-preset-es2015,官网上说的命令为npm install babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env webpacknpm install babel-loader babel-core babel-preset-env webpack,我们这里为了将babel-loader的版本和我们的webpack版本一致,采用7的版本,然后这个babel-loader还需要一些核心的东西就是后面跟着的babel-core,再后面的babel-preset-env是一些配置的东西,如果是typescript的转换则采用不同的配置文件,然后官网后面还跟着webpack这里我们已经有了就不需要了,安装完成之后,还要进行配置,代码如下:

1
2
3
4
5
6
7
8
9
10
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}

这时候我们再进行打包的时候bundle.js文件中就不会再有ES6相关的语法了。


- webpack中配置vue

接下来我们就可以在webpack中配置我们的vue环境了,当然我们首先也要安装有关vue的相应的包及相应的loader,安装命令为npm install vue --savenpm install vue-loader vue-template-compiler --save-dev(执行完后需要修改package.json文件中"vue-loader": "^13.0.0",因为14.0以上版本需要安装其它插件),我们也注意到前一个命令没有加–dev说明它在运行时也是需要我们的vue的,然后我们就可以重新打包文件了,在浏览器中打开文件,我们发现文件并不能运行,并且在控制台报错了,控制台显示我们正在使用的是tuntime-only版本,让我们使用runtime-compiler版本,实际上vue在构建的时候,构建了以上两个版本,如果我们使用第一个版本,这就代表代码中不能有任何template,这个版本就没有关于编译template的文件,只有使用runtime-compiler版本,才能编译template代码,那们怎么使用第二个版本呢,这时候就需要相应的配置了,代码如下:

1
2
3
4
5
6
7
8
resolve:{
// 导入的时候省略后缀
//extensions:['.js','.css','.vue'],
// 别名
alias:{
'vue$': 'vue/dist/vue.esm.js'
}
}

完成以上配置后,我们再次打包文件就不会出现以上的情况了,然后就是Vue相关的编写了,这里我就不说明怎么一步步抽取出来了,直接上代码吧,首先在src中新建Vue文件夹,创建两个vue文件 Cpn.vue和Tpl.vue

Cpn.vue代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>
<p>我是Cpn组件</p>
<p>大家好啊,初次见面嗷</p>
<button @click="hello">我是Cpn组件的按钮</button>
</div>
</template>

<script>
export default {
methods: {
hello(){
alert('hello');
}
},
}
</script>

<style>

</style>

Tpl.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
<div>
<h1 class="yes">{{message}}</h1>
<h1>{{date}}</h1>
<Cpn/>
</div>
</template>

<script>
import Cpn from './Cpn.vue';

export default {
data(){
return {
message: 'webpack',
date: '2020-02-13'
}
},
components:{
Cpn
}
}
</script>

<style>
.yes{
color:aqua
}
</style>

main.js代码(主要看最后关于Vue的部分即可)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 1.使用CommonJs方式导入
const {add,Unadd} = require('./js/info.js');
add(10,20);
Unadd(20,10);

// 2.使用ES6方式导入
import {chu} from './js/math.js';
chu(20,2);


// 导入css文件
require('./css/nomal.css');

//导入less文件
require('./css/special.less');
document.writeln('<h1>hello beanBag</h1>');

// 导入vue文件
import Vue from 'vue';
// import Tpl from './vue/Tpl'
import Tpl from './vue/Tpl.vue'

new Vue({
el: '#container',
template: '<Tpl/>',
components: {
Tpl
}
})

在加载.vue文件时还需要进行如下配置:

1
2
3
4
{
test: /\.vue$/,
use:['vue-loader']
}

这里有一细节问题,就是在导入的时候如果你不想写文件后缀的话,可以添加下面这行配置:

1
2
3
4
5
6
7
8
resolve:{
// 导入的时候省略后缀
extensions:['.js','.css','.vue'],
// 别名
alias:{
'vue$': 'vue/dist/vue.esm.js'
}
}

- webpack中Plugin的使用

plugin是插件的意思,它是对webpack本身的一种拓展,是一个扩展器,而loader是用于转换某些类型的模块,是一个转换器

今天只学习了一种插件,BannerPlugin用于添加版权信息
首先应该在webpack.config.js文件中引入webpack模块,并在plugin属性中做相应的设置,webpack.config.js文件完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname,'dist'),
filename: 'bundle.js',
// 涉及到路径 他都会在前面加一个dist 最后就不需要了
publicPath: 'dist/'
},
module: {
rules: [
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.less$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader" // compiles Less to CSS
}]
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// 当加载的图片大小小于limit时,他会将图片编译成base64字符串形式 不需要单独的文件来存储
// 当加载的图片大小大于limit时,使用file-loader模块进行加载 会单独打包成另一个文件
// 这个值默认是8k
limit: 8192,
name: 'img/[name][hash:8].[ext]'
}
}
]
},
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
},
{
test: /\.vue$/,
use:['vue-loader']
}
]
},
resolve:{
// 导入的时候省略后缀
extensions:['.js','.css','.vue'],
// 别名
alias:{
'vue$': 'vue/dist/vue.esm.js'
}
},
plugins:[
new webpack.BannerPlugin('最终版权归beanBag所有')
]
}

这里注意最后一行即可,这时候当我们重新打包文件的时候,在你打包好的文件的第一行就会出现你在BannerPlugin里写的那行话。

Vue.js学习第九天

webpack插件相关、配置分离及CLI的安装

继昨天学习的BannerPlugin继续今天有关插件的学习吧!

  1. webpack插件相关
  • HtmlWebpackPlugin

    作用:自动生成一个index.html文件(可以指定模板来生成)

    安装:npm install html-webpack-plugin --save-dev

    配置:下载完成之后,同样需要在webpack.config.js文件中进行引入,并且在plugins中进行相应的配置,const HtmlWebpackPlugin = require('html-webpack-plugin');

    1
    2
    3
    new HtmlWebpackPlugin({
    template:'index.html'
    })

    template的作用是指明它的模板,模板中的script标签可以省略,因为此插件会自动将用到的script进行引入,之后我们重新打包文件后就会自动在dist目录中生成一个新的index文件。注:这里要把之前的publicPath注释掉,因为bundle.js文件已经和index文件在同一级目录下了

  • UglifyjsWebpackPlugin

    作用:对打包的js文件进行压缩

    安装:npm install uglifyjs-webpack-plugin@1.1.1 --save-dev

    配置:下载完成后,需要在webpack.config.js文件中进行引入,并且在plugins中进行相应的配置,**const** UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');

    1
    2
    3
    plugins:[
    new UglifyjsWebpackPlugin()
    ],

    配置后,重新打包文件我们就会发现js文件已经删去空格以及我们之前用BannerPlugin生成的版权说明,所以此插件最好不要和BannerPlugin一起使用

  • WebpackDevServer

    作用:搭建本地服务器,能够自动监听代码是否有改变,并实时地在界面上进行渲染,就不用我们每一次都对文件进行打包了

    原理:它是基于node.js的,内部使用的是node中的express框架,当它监听到有改变的时候,就会重新进行编译,将编译后的代码放在内存中让我们进行测试,最终只需要执行一次对文件的打包即可

    安装:npm install --save-dev webpack-dev-server@2.9.3

    配置:下载完成后,需要在webpack.config.js文件中进行相应的配置

    1
    2
    3
    4
    devServer:{
    contentBase:'./dist', //指明服务于哪一个文件夹
    inline:true //是否需要实时刷新
    }

    此外还有两个可选参数

    1. port:指明它的端口号,默认是8080
    2. historyApiFallback:在SPA(单页面复应用)页面中,依赖HTML5的history模式

    配置完成后,有两种方式对我们的文件进行实时的刷新

    1. 采用命令./node_modules/.bin/webpack-dev-server
    2. 在package.json文件的script中,添加"dev": "webpack-dev-server --open"–open是用来当输完命令后它会自动打开浏览器,若没有写,则需要我们点击链接进入

    注意:不能在终端中直接输入命令webpack-dev-server因为之前我们说过,在任何的终端中输入的命令都会去全局下找对应的文件,但是我们之前并没有全局安装


  1. webpack配置文件的分离

​ 背景:在我们完成上述操作后,我们会发现,在我们的开发模式中,对于uglifyjs-webpack-plugin插件的使用其实是用不到的,因为实际开发中,要反复调试我们的js代码,故应该省去此插件的使用;同理在我们的运行环境下本地服务器插件也是用不到的,所以将webpack配置文件的分离是非常必要的。

​ 思路:我们将开发时用到的一些模块放到dev-config.js文件中,将运行时用到的文件放到prod.config.js文件中,将一些基本的代码放到base.config.js文件中,这样一来一旦我们是在开发环境中,执行开发环境的命令时,就将dev-config.js和base.config.js文件合并在一起,在运行环境中也是同理,那么什么插件可以将我们的两个文件进行合并呢,那就是webpack-merge

​ 安装:npm install webpack-merge --save-dev

​ 配置:新建build文件夹,文件夹中存放以上说的三个文件,代码下边我会贴出来,在dev-config.js文件和prod.config.js文件中需要引入我们安装的webpack-merge,还是贴代码吧

base.js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname,'../dist'),
filename: 'bundle.js',
// 涉及到路径 他都会在前面加一个dist 最后就不需要了
// publicPath: 'dist/'
},
module: {
rules: [
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.less$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader" // compiles Less to CSS
}]
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
// 当加载的图片大小小于limit时,他会将图片编译成base64字符串形式 不需要单独的文件来存储
// 当加载的图片大小大于limit时,使用file-loader模块进行加载 会单独打包成另一个文件
// 这个值默认是8k
limit: 8192,
name: 'img/[name][hash:8].[ext]'
}
}
]
},
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
},
{
test: /\.vue$/,
use:['vue-loader']
}
]
},
resolve:{
// 导入的时候省略后缀
extensions:['.js','.css','.vue'],
// 别名
alias:{
'vue$': 'vue/dist/vue.esm.js'
}
},
plugins:[
new webpack.BannerPlugin('最终版权归beanBag所有'),
new HtmlWebpackPlugin({
template:'index.html'
})
]
}

dev.cofig.js文件

1
2
3
4
5
6
7
8
9
const webpackMerge = require('webpack-merge');
const baseConfig = require('./base.config');

module.exports = webpackMerge(baseConfig,{
devServer:{
contentBase:'./dist',
inline:true
}
})

prod.config.js文件

1
2
3
4
5
6
7
8
9
const webpackMerge = require('webpack-merge');
const baseConfig = require('./base.config');
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');

module.exports = webpackMerge(baseConfig,{
plugins:[
new UglifyjsWebpackPlugin()
]
})

这时候当我们重新打包后,终端也会报错,终端说找不到我们的webpack.config.js文件,所以我们要到package.json文件的scripts中做进一步的配置,代码如下:

1
2
3
4
5
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config ./build/prod.config.js",
"dev": "webpack-dev-server --open --config ./build/dev.config.js"
}

这时候我们重新打包就不会报错了,但是会出现另外一个问题,就是打包生成的文件跑到了build文件夹下,因为base文件中指定目录是当前所在的目录,所以我们还需要修改base文件里的目录拼接部分path: path.resolve(__dirname,'../dist'),这时候再打包就可以了。现在我们就实现了一个简单的配置分离的工作


  1. vuecli脚手架相关

背景:从以上webpack的配置来看,是不是感到非常的繁琐,事实上,当我们配置大型项目的时候,配置往往更加复杂,所以出现了我们的cli,也就是现在俗称的脚手架,当然并不是它的全称,而是它能够一次性地搭建出完整的骨架,所以形象地把它称为脚手架

安装:npm install -g @vue/cli@3.2.1我们之所以安装3.2.1版本而不是最新的版本是为了和老师地保持同步,免得又出岔子,由于后面老师会先讲vue2的搭建,所以这里还需要拉取cli2.x的模板,命令为npm install @vue/cli-init -g

介绍:CLI:Command-Line Interface 叫做命令行界面,VueCLI是官方发布的vue.js项目脚手架,使用脚手架可以快速搭建vue开发环境以及对应的webpack配置,当我们在编写大型项目中,往往需要考虑代码的目录结构、项目结构和部署、热加载以及代码的单元测试,脚手架帮我们考虑到了以上的问题,是一个非常好的工具

使用:接下来我们就创建一个脚手架2的项目,脚手架2和脚手架3在创建项目的时候,命令有点差别
脚手架2:vue init webpack my-project
脚手架3:vue create my_project
安装过程中需要我们回答一些问题,稍微有一个看不懂的是Use ESLint to lint your code?这个指的是代码的规范,我们一般喜欢随心所欲所以回答No即可,还有一个单元测试,我们也选择No吧,完成后会生成以下图片的一个项目结构
在这里插入图片描述

Vue.js学习第十天

webpack中VueCLI3的创建及ES6相关

一. runtime-compiler和runtime-only的区别

​ 在安装CLI2的版本的时候,我们同时安装了一个选择runtime-compiler另一个选择runtime-only的版本,我们现在来对比一下它们的区别

​ runtime-compiler版本:

1
2
3
4
5
6
7
8
9
10
11
12
import Vue from 'Vue'
import App from './App'

Vue.config.productionTip = false;

new Vue({
el: '#app',
template: '<App/>',
components: {
APP
}
})

​ runtime-only版本:

1
2
3
4
5
6
7
8
9
import Vue from 'Vue'
import App from './App'

Vue.config.productionTip = false;

new Vue({
el: '#app',
render: h => h(App)
})

​ 我们发现在main.js文件中两者在创建Vue实例的属性和参数是不一样的,下面我们可以来分析一下两者的不同,实际上,在runtime-compiler版本中Vue内部的处理过程是这样的:

  1. Vue将template中的各个元素解析成AST(Absoult Syntax Tree)抽象语法树;
  2. 抽象语法树被编译成render函数;
  3. render函数被渲染成virtual dom虚拟dom元素;
  4. 浏览器将虚拟dom元素展示在界面上;

​ 而我们的runtime-only版本会省略第一、二步,直接通过render函数渲染成虚拟dom,并展示在界面上,由此我们可以发现,runtime-only版本的性能较高,代码量也较少,所以在选择的时候它会提示我们选择runtime-only版本的文件会少6kb。

​ 以上的h函数实际上就类似于createElement函数,它有两种用法:

  1. createElement('标签名',{标签的类型},[标签的值])例如:

    1
    2
    3
    render : function (createElement){
    return createElement('h2',{class:'box'},['hello']);
    }
  2. createElement(组件的名称)例如:

    1
    2
    3
    render : function (createElement){
    return createElement(App);
    }

​ 在项目运行时,runtime-only版本中会将template预编译成JavaScript,一旦打包时,已经是编译完成的版本,浏览器不用做编译的步骤;runtime-compiler版本中并不是在打包时进行编译的,而是在客户端使用其自带的compiler进行编译。

那么,runtime-compiler版本中的.vue文件中的template是由谁处理的呢

实际上是由 vue-template-compiler 处理的。所以现在当我们了解了它们的区别后,会发现选择runtime-only更加合理。

二. VueCLI3的创建及使用

  • 安装命令:vue create my-project

  • 配置:脚手架3的配置相较于脚手架2来说更加简洁一些,我们在选择安装的其他插件的时候勾选一个Babel即可;

  • 与CLI2有哪些改变?

    1. vue-cli3是基于webpack4打造,vue-cli2还是webpack3;
      1. vue-cli3的设计原则是“0配置”,移除了配置文件根目录下的build和config等目录;
        1. vue-cli3提供了vue ui命令,提供了可视化的配置,更加人性化;
          1. vue-cli3移除了static文件夹,新增public文件夹,并且index.html移动到public中。
  • 配置文件的查看与修改:

    方法一:通过命令 vue ui 通过可视化界面来修改配置文件;

    ​ 方法二:在 node_modeules/@vue/cli-service/webpack.config.js;

    ​ 方法三:在自己项目的根目录下,创建vue.config.js文件,添加自己的配置即可。

三. ES6语法相关

1. 箭头函数的使用和this的指向

​ 使用就不赘述了,注意一下,没有参数的时候不用写(),没有返回值的时候不用 { }即可

​ 这里讲一下箭头函数中this的指向,箭头函数中的this引用的就是最近作用域中的this,见以下 案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<script>
const obj = {
aaa() {
setTimeout(function () {
setTimeout(function () {
console.log(this);
})

setTimeout(() => {
console.log(this);
})
})

setTimeout(() => {
setTimeout(function () {
console.log(this);
});
})

setTimeout(() => {
console.log(this);
});
}
}
obj.aaa();
</script>

这里前三个的this指向的都是window,最后一个this指向的是obj对象,当我们使用funciton时,内部自动会传入window对象,所以不论怎么变化,funciton中的this指向的都是window而箭头函数的this就会往他上一级找,直到找到有this的定义,类似于冒泡出去

2.作用域的问题

在ES5之前只有函数有作用域的概念,而for循环和if都没有块级作用域,到ES6之后,使用let就 会有块级作用域,所以在ES6中我们优先使用const,只有需要改变某一个标识符的时候才使用let。

3.高阶函数

1.filter:对数据进行过滤,filter中的回调函数必须返回一个布尔值

​ true:函数内部会自动将这次回调的n创建一个新的数组;

​ false:函数内部会自动过滤掉这次的n

2. map: 对数据进行相关处理

3.reduce:对数组中的数据进行汇总,详情见下面的一个例子:

​ 例子中要求先将小于100的数筛选出,再对小于100的数乘以2,最后将乘以2的数字相加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<script>
const array = [10,20,30,50,111,20,30,555,666,20];
// --------------写法一--------------------------
// // 1. filter的使用 必须返回的布尔值
// // 若返回true 函数内部会自动将这次回调的数据创建一个新的数组并保存
// // 若返回false 函数内部会过滤掉这次的数据
// let array2=array.filter(function(n){
// return n<100;
// });
// console.log(array2);

// //2. map的使用 对数据进行相关处理
// let array3 = array2.map(function(n){
// return n*2;
// //return 100;
// });
// console.log(array3);

// //3.reduce的使用 对数据进行汇总
// //array3=[20,40,60,100,40,60,40]
// //第一次: pre:0 n:20
// //第二次: pre:20 n:40
// //第三次: pre:60 n:60 ......
// let total = array3.reduce(function(previous,n){
// return n+previous;
// },0);
// console.log(total);

// --------------写法二--------------------------
// let total = array.filter(function(n){
// return n<100;
// }).map(function(n){
// return n*2;
// }).reduce(function(pre,n){
// return pre+n;
// },0);
// console.log(total);

// --------------写法三--------------------------
let total = array.filter(n => n<100).map(n => n*2).reduce((pre,n) => pre+n);
console.log(total);

</script>

Vue.js学习第十一天

vue中的路由基本使用

一. 概念相关

  • 什么是路由

    ​ 路由就是通过互联的网络把信息从源地址传输到目的地址的活动(维基百科)

    ​ 路由器提供了两种机制,路由和转送

    1. 路由:路由是决定数据包从来源到目的地的途径

    2. 转送:转送是将输入端的数据转移到合适的输出端

      路由表:本质上就是一个映射表,决定了数据包的指向

  • 什么是前端渲染、前端路由、后端渲染、后端路由

    在网站开发早期阶段(后端路由阶段),整个html页面是由服务器来渲染的,并返回给客户端进行展示,这种由服务器完成网址与对应页面的映射叫做后端路由,当然缺点也是非常明显的,后端人员需要负责整个页面的开发,对前端开发人员要求高,html代码和数据混合在一起都非常糟糕,到了网站开发的中期(前后端分离阶段),随着Ajax技术的出现,有了前后端分离的开发模式,后端专注于数据的处理,而前端专注页面的交互和可视化上,目前很多的网站依然采用这种模式,到目前的一部分公司已经到了单页面富应用阶段(SPA),也就是在前后端分离的基础上加了一层前端路由,也就是前端来维护一套路由规则。

二. 如何改变URL并使页面不刷新

  • hash

    vue-router默认的使用方式,地址栏会有一个#号,在浏览器中可以使用location.hash = 'home'来观察其模式,使用这个模式可以实现

  • HTML5的history模式(地址栏不会出现# 所以较多使用)

    1. history.pushState({},'','home')它采用的是栈结构,可以记录我们浏览的历史,我们可以用前进和后退的方式对页面进行访问。
    2. history.replaceState({},'','home')和第一个pushState模式不同的是他不能使用我们的前进和后退对我们的页面进行访问
    3. history.go(-1)栈中弹出一个地址
    4. history.back此方法等价于history.go(-1)
    5. history.forward此方法等价于history.go(1)

三.vue-router安装和配置方式

  • 概念

    Vue中的route是基于路由和组件的,路由用于设定访问路径,将路径和组件映射起来,在vue-router的单页面富应用中,页面的路径的改变就是组件的切换

  • 安装:npm install vue-router --save

  • 使用:

    1. 导入路由对象,并且调用Vue.use(插件)安装插件
    2. 创建路由实例,并且传入路由映射配置
      2.1. 创建路由组件
      2.2. 配置路由映射,即组件和路径的映射关系
      2.3. 使用路由,通过 router-linkrouter-view标签
    3. 在Vue实例中挂在创建的路由实例

    下面是完整的vue-router代码 router文件夹下的index.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    import VueRouter from 'vue-router'
    import Vue from 'vue'
    import index from '../components/index.vue'
    import about from '../components/about.vue'

    // 1.导入路由对象 安装插件
    Vue.use(VueRouter)

    // 2.创建路由实例,并且传入路由映射配置
    const routes = [
    {
    path:'',
    redirect: '/home'
    },
    {
    path: '/home',
    component: index
    },
    {
    path: '/about',
    component: about
    }
    ]

    const router = new VueRouter({
    routes,
    // mode: 'history',
    // linkActiveClass: 'active'
    })

    // 3.在Vue实例中挂载创建的路由实例
    export default router

    App.vue代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <template>
    <div id="app">
    <router-link to="/home">首页</router-link>
    <router-link to="/about">关于</router-link>
    <router-view></router-view>
    </div>
    </template>

    <script>
    export default {
    name: 'App',
    }
    </script>

    <style>
    </style>

    components文件夹下的about.vue组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <template>
    <div>
    <p>我是关于组件嗷 别认错了</p>
    </div>
    </template>

    <script>
    export default {
    name: 'about'
    }
    </script>

    <style>

    </style>

    components文件夹下的index.vue组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <template>
    <div>
    <p>我是首页组件嗷</p>
    </div>
    </template>

    <script>
    export default {
    name: 'index'
    }
    </script>

    <style>

    </style>

    main.js文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import Vue from 'vue'
    import App from './App'
    import routes from './router/index.js'

    Vue.config.productionTip = false

    /* eslint-disable no-new */
    new Vue({
    el: '#app',
    router:routes,
    render: h => h(App)
    })

    注意:这里面有一个坑,在创建路由实例的时候,让我们传入路由映射配置时,这里我写的是routes,倘若你想叫其他名字,就必须写成routes: 你的名字。否则就必须定义为routes,main.js中挂载实例的时候也同理,简写可以直接写router,如果你要自己命名,就必须写成router: 你的名字,千万要注意!

  • 一些参数的问题

    1. 在vue-router中,可以添加一个默认的路径,比如开发过程中我们需要用户已进入我们的网页首先就跳转到某一个页面时,就需要我们添加一个默认的路径,这时你需要再配置一个映射关系,代码如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      const routes = [
      {
      path:'', //此处不写就代表一个默认路径,或者只写一个/
      redirect: '/home' //此处指默认路径重定向的路径
      },
      {
      path: '/home',
      component: index
      },
      {
      path: '/about',
      component: about
      }
      ]
    2. 在vue-router的默认配置中是使用hash模式的,我们可以将它修改为history模式,只需要在创建实例的时候再传入一个参数,如下:

      1
      2
      3
      4
      5
      const router = new VueRouter({
      routes,
      mode: 'history', //将模式修改为history
      // linkActiveClass: 'active'
      })
    3. router-link的其它属性

      • tag:可以指定router-link之后在页面上不被渲染成a标签,可以指定渲染成按钮等其它标签

        <router-link to='/about' tag="button">关于</router-link>

      • replace: 不会留下history记录,也就是不能使用前进后退键跳转页面

        <router-link to='/about' replace>关于</router-link>

      • active-class: 当我们选中标签时,默认会将此标签新增一个router-link-active样式,我们可以重新命名这个样式,只需要使用这个属性(以下这个案例就被修改为active)

        <router-link to='/home' active-class='active'>首页</router-link>

        当我们需要修改多个名称时,不必每一个都这样修改,可以直接在创建实例的时候传入一个属性,如下:

        1
        2
        3
        4
        5
        const router = new VueRouter({
        routes,
        mode: 'history',
        linkActiveClass: 'active'
        })
  • 通过代码跳转页面

    有时候我们没有在模板中定义router-link这个标签,我们可以使用通过代码跳转页面的方式进行,比如我们定义的是button标签,这时候我们只要监听button的点击事件,并在事件中使用$router属性即可实现页面跳转,此属性是vue-router给每一个组件中都会默认添加的属性,使用代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <template>
    <div id="app">
    <router-link to="/home">首页</router-link>
    <router-link to="/about">关于</router-link>
    <router-view></router-view>
    </div>
    </template>

    <script>
    export default {
    name: 'App',
    methods: {
    clickHome(){
    this.$router.push('/home');
    // this.$router.replace('/home');
    console.log('aa');
    },
    clickAbout(){
    this.$router.push('/about');
    // this.$router.replace('/about');
    console.log('bb');
    }
    },
    }
    </script>

    <style>
    </style>

    同样的我们也可以使用replace等方法进行控制页面跳转。

Vue.js学习第十二天

动态路由、懒加载、嵌套路由及传递参数相关

. 动态路由

  
1
2
3
4
5
6
7
8
export default {
name: 'App',
data() {
return {
userId: 'beanBag'
}
}
}

. 路由懒加载

  • 背景:当我们打包构建应用时,JavaScript包会变得非常大,影响页面加载,所以必须由一种方法来提升用户体验
  • 了解: 在我们打包好的Js文件中我们会发现有三个文件,分别是app、manifest和vendor,这三个Js文件分别负责我们的业务代码、为打包代码做底层支撑以及第三方的一些代码(vue,axios…)**,app中将我们的不同文件作为数组的参数传入相关函数中处理,所以如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问时才加载对应组件**,这样就能解决此问题
  • 使用: 1. 结合Vue的异步组件和Webpack的代码
    1. AMD写法
    2. ES6写法(推荐使用)
      const index = () => import('../components/index.vue'); //导入的时候就指定为懒加载

. 嵌套路由

  • 背景:当我们访问一个页面的时候,这个页面中有很多小的页面,倘若我们向以前一样配置映射关系,那么所有的路径都会是处于同一级别,不利于我们的管理,如果能够在大的路径下划分小的路径,就能方便我们的管理

  • 使用: 1. 创建两个组件indexNews.vue和indexMessage.vue这两个组件是index界面下的两个子组件;

    1. 配置映射关系:
   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
path: '/home',
component: index,
children: [
{
path: 'message',
component: indexMessage
},
{
path: 'news',
component: indexNews
}
]
},
  1. index.vue中创建这两个文件的入口

    1
    2
    3
    <router-link to='/home/news'>新闻</router-link>
    <router-link to='/home/message'>消息</router-link>
    <router-view></router-view>
    • 拓展:为嵌套路由添加默认路径,在children中添加默认路由的配置即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
path: '/home',
component: index,
children: [
{
path: '',
redirect: 'message'
},
{
path: 'message',
component: indexMessage
},
{
path: 'news',
component: indexNews
}
]
},

. 传递参数

  • 使用:有 paramsquery 两种类型的传递方式
  • 比较
类型paramsquery
配置路由格式/router/:id/router(一般配置)
传递方式在path后面跟上对应的值对象中使用query的key作为传递方式
传递后形成的路径/router/beanBag/router?id=beanBag
使用场景数据传递较少数据传递较多
  • query方式的使用

    1. 创建profile组件

    2. 配置路由映射关系

    3. 在APP.vue中创建一个button(或者直接rputer-link标签),button监听点击事件profileClick方法,在方法中写相关代码传入参数

  
1
2
3
4
5
6
7
8
9
10
11
methods: {
profileClick(){
this.$router.push({
path: '/profile',
query: {
name: 'beanBag',
age:18
}
})
}
}
  1. 在profile.vue界面中使用参数

    1
    2
    3
    4
    5
    6
    7
    <template>
    <div>
    <h3>我是profile组件</h3>
    <p>{{$route.query.name}}</p>
    <p>{{$route.query.age}}</p>
    </div>
    </template>

**要注意,在使用代码方式进行路由跳转的时候使用的是$router,在拿到传入数据时使用的是$route**,$router对象是vue-router中的一个实例,在整个vue-router实例中都会存在,而$route只有在处于活跃路由时才会存在于对应的那个路由

Vue.js学习第十三天

导航守卫、keep-alive、项目相关

一. 导航守卫

  • 背景:有时候在我们的项目中,需要在跳转路由的时候进行相关操作,比如有这么一个需求,当我们跳转页面时,页面的title会随之发生改变,这时候就要用到我们的导航守卫。导航守卫主要是用来监听路由的进入和离开,vue-router提供的beforeEach和afterEach的钩子函数,他们会在路由即将改变前和改变后触发

  • 相关知识:

    1. 生命周期函数
       在Vue中有几个生命周期函数需要我们了解一下
       ``created() ``: 组件被创建完成后回调
       ``destroyed() ``: 组件被销毁后回调
       ``mounted() ``: 创建组件后(template)挂载在DOM上回调
       ``updated() ``: 界面发生更新时回调
       几个生命周期函数配合我们的全局守卫是比较好用的,上面那个案例我们使用created函数也是可以解决的,但是一旦项目较大的时候,会比较不方便
      1. 全局守卫
        beforeEach(),在源码中描述它为guard,也就是守卫
        afterEach 在源码中描述它为hook,也就是钩子
        在以上两个全局守卫中都需要我们传递一个匿名函数进行回调,在上面那个函数也就是router.beforeEach()需要我们往匿名函数中传递三个参数(to,from,next),我们必须在手动调用next(),使它进行路由跳转,下面一个函数就不需要传递next
        to: 代表即将要进入的目标的路由对象
        from: 当前导航即将要离开的路由对象
        next: 调用该方法后才能进入下一个钩子
      2. 路由独享的守卫及组件内的守卫
        在vue中还存在有其它的守卫,也就是路由独享的守卫和组件内的守卫,路由独享的守卫比如有 beforeEnter,组件内的守卫比如有 beforeRouterEnter及beforeRouterUpdate,这些了解即可,用到的时候可以翻阅文档
  • 使用
    在配置路由的index.js中,使用相关的导航守卫即可,这里使用beforeEach做示范

1
2
3
4
5
router.beforeEach((to, from, next) => {
// to and from are both route objects. must call `next`.
document.title = to.matched[0].meta.title;
next();
})

meta是在配置路由时加入的数据里面包含有title,matched[0]的使用时由于存在嵌套路由,所以拿不到首页的title,所以我们要到matched数组中拿到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const routes = [
{
path:'',
redirect: '/home',

},
{
path: '/user/:userId',
component: user,
meta: {
title: '用户'
},
// beforeEnter: (to, from, next) => {
// console.log('我是路由独享守卫');
// next();
// }
},
{
path: '/home',
component: index,
children: [
{
path: '',
redirect: 'message'
},
{
path: 'message',
component: indexMessage
},
{
path: 'news',
component: indexNews
}
],
meta: {
title: '首页'
}
},
{
path: '/about',
component: about,
meta: {
title: '关于'
}
},
{
path: '/profile',
component: profile,
meta: {
title: '档案'
}
}
]

二. keep-alive

  • 背景:在我们前几天的学习中,曾经提过一个需求我们当时没有实现,也就是在一个页面中存在嵌套路由,当我们点击嵌套路由中的那个路由时,跳转到其它路由,我们希望在跳转回来时,界面仍然跳转到之前访问的嵌套路由的那个页面,这就需要我们的keep-alive,它是Vue内置的一个组件,可以使被包含的组件保留状态或避免重新渲染
  • 使用:此函数的使用较为简单,只需要在<router-view>标签的外部包一层keep-alive即可,这里我们结合了组件内的守卫beforeRouteLeaveactivated的使用完成了上面那个需求,在首页这个组件中使用组件内的守卫beforeRouteLeave拿到现在访问的路径,当再次访问此组件时,使用activated手动进行路由跳转,路径指定为使用组件内的守卫拿到的那个路径即可
1
2
3
4
5
6
7
activated() {
this.$router.push(this.path);
},
beforeRouteLeave (to, from, next) {
this.path = this.$route.path;
next();
}
  • 补充:需要注意的是activated()和deactivated()两个函数只有在该组件为保持状态时,也就是使用了keep-alive时才有效,否则都是重新创建,并不是处于活跃的。
    另外这个keep-alive标签中有两个属性,includeexclude包含和排除,后面可以跟字符串或者是正则表达式,只有匹配到的组件才会被缓存或是不缓存,多个组件之间用逗号分隔,不能有空格。例如
1
2
3
<keep-alive exclude="user,profile">
<router-view></router-view>
</keep-alive>

三. 项目相关(TabBar的封装)

这是后面需要完成的一个项目中的一个组件,到最后我再把整个项目一起放上来好了

Vue.js学习第十四天

Promise的使用及了解Vuex

一、 Promise的使用
       【背景】:Promise是ES6中的新特性,是异步编程的一种解决方案,一种很常见的应用场景就是网络请求,如果当我们的网络请求非常复杂时,就会出现回调地狱,就是如下代码的一种情况:

1
2
3
4
5
6
7
8
9
10
11
<script>
$.ajax('url1',function(data1){
$.ajax(data1['url2'],function(data2){
$.ajax(data2['url3'],function(data3){
$.ajax(data3['url4'],function(data4){
console.log(data4);
})
})
})
})
</script>

在第一次网络请求到的数据是第二次网络请求的参数,一直这么下去,被称为回调地狱,说白了就是不能无限套娃,否则代码难以维护,我们希望以一种更加优雅的方式来进行异步操作,所以就出现了promise

使用】:

  • 基本使用
1
2
3
4
5
6
7
8
9
<script>
new Promise((resolve,reject) => {
setTimeout(() => {
resolve()
}, 1000);
}).then(()=>{
console.log('Promise示例');
})
</script>
  • 嵌套使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
new Promise((resolve,reject) => {
setTimeout(() => {
resolve()
}, 1000);
}).then(()=>{
console.log('1');
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve()
}, 1000);
})
}).then(()=>{
console.log('2');
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve()
}, 1000);
})
}).then(()=>{
console.log('3');
})
</script>

在promise函数中需要我们填写两个参数,这两个参数对应也是函数,当我们执行resolve时,会自动调用then函数,如果调用reject时,会调用catch函数处理失败的情况,如下(控制台打印‘出错啦’):

1
2
3
4
5
6
7
8
9
10
11
<script>
new Promise((resolve,reject)=>{
setTimeout(() => {
reject('出错啦!')
}, 1000);
}).then(()=>{
console.log('成功')
}).catch((data)=>{
console.log(data)
})
</script>

三种状态

  1. pending:等待状态,比如正在进行的网络请求,或者定时器没有到时间
  2. fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()
  3. reject:拒绝状态,当我们主动毁掉了reject时,就处于该状态,并且会回调.catch()
    注意:promise中的.then可以有另一种写法,如下:
1
2
3
4
5
6
7
8
9
10
11
12
<script>
new Promise((resolve,reject)=>{
setTimeout(() => {
// resolve('这是resolve函数');
reject('这是reject函数');
}, 1000);
}).then(data=>{
console.log(data)
},err=>{
console.log(err);
})
</script>

案例】下面有一个需求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// wrapped into 包裹 将异步操作包裹在promise里面
// 网络请求 aaa->自己处理第一次
// 处理:aaa1 -> 自己处理第二次
// 处理:aaa12 -> 自己处理第三次

//大致意思就是将网络请求回来的数据自己处理完成后做相应的转换然后传给下一次,
//要求将转换操作和处理操作相分离
<script>
new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('aaa');
}, 1000);
}).then((res)=>{
console.log(res,'第一次处理');
return new Promise((resolve,reject)=>{
resolve(res+'1');
})
}).then(res=>{
console.log(res,'第二次处理');
return new Promise((resolve,reject)=>{
resolve(res+'2');
})
}).then(res=>{
console.log(res,'第三次处理');
})
</script>

第一次拆分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- new Promise(resolve => resolve(结果))简写 -->
<script>
new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('aaa');
}, 1000);
}).then((res)=>{
console.log(res,'第一次处理');
return Promise.resolve(res+'1');
}).then(res=>{
console.log(res,'第二次处理');
return Promise.resolve(res+'2');
}).then(res=>{
console.log(res,'第三次处理');
})
</script>

第二次简写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 省略掉Promise.resolve -->
<script>
new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('aaa');
}, 1000);
}).then((res)=>{
console.log(res,'第一次处理');
return res+'1';
}).then(res=>{
console.log(res,'第二次处理');
return res+'2';
}).then(res=>{
console.log(res,'第三次处理');
})
</script>

如果中间出现了reject同理,第三种改为throw 'error message'就不用写Promise.reject('error message')

all方法的使用
对多个异步的处理,比如现在有两个网络请求,只有两个请求的数据都拿到了才能显示页面,所以出现了这个all方法,只有两个请求都完成才能完成此需求,all方法会判断两个异步操作是否完成,拿到的是一个数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
Promise.all([
new Promise((resolve,reject) => {
setTimeout(() => {
resolve('result1')
}, 1000);
}),
new Promise((resolve,reject) => {
setTimeout(() => {
resolve({
name: 'beanBag',
age:22
})
}, 2000);
})
]).then(result =>{
console.log(result);
})
</script>

二、了解Vuex
       【官方解释】Vuex是一个专门为Vuex.js应用程序开发的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化,说的简单一点,就是把大家都要互相用的东西放到一个文件中,这个文件统一来管理这些东西,并且可以很方便的为大家调用并且是响应式
       【应用场景】用户的登陆状态、用户名称、头像等等,商品的收藏,购物车的物品等
       【使用】同vue-router一样,vuex是vue提供的一种插件,所以使用的步骤和vue-router类似,代码就明天再贴吧

三、项目中的小知识点
项目中如果遇见路径太复杂的话,我们可以将路径起一个别名,比如图片的路径每次都是../../找起来非常麻烦,所以我们可以给他起一个别名

  1. 修改webpack.base.conf.js中的resolve
1
2
3
4
5
6
7
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
'assets': resolve('src/assets'),
}
},

2.将你希望替代的别名写入,比如可以用assets代替src/assets这个路径
3.使用,当我们起了别名后,在引用的时候如果实在html代码中和属性中填写路径,要注意使用~符号,(在script标签中不用加),否则是不生效的
<img src="~assets/img/tabbar/home.svg" slot="item-icon">