Vue 复习
B站课程二刷
1.计算属性computed
- 计算属性,是一种定义在computed里面的属性,其中有set和get方法,但是set一般都不使用,所以我们一般都不写,然后get属性,我们为了方便的话都使用,都是把get直接换成变量名的形式。所以我们在通过must语法定义值的时候都不会带括号
computed:{
fullName:{
set:function(){
#set 一般都不使用,省略
},
get:function(){
#而get我们一般都省略
}
}
}
省略之后变成这种方式,所以fullName 是计算属性。
computed:{
fullName:function(){
}
}
- 计算属性computed,比methods方法要好,因为computed方法有缓存,多次使用只会调用一次,而methods每一次使用都会重新调用。
2.Watch属性
- 通过Watch 可以动态带检测到data里面的属性变化,当属性变化时就会调用watch属性里面的方法。但是方法的名字要与被检测的data变量一致。
3.编译作用域
- 编译作用域就是在模板里面使用变量的时候,会首先看是这个模板在谁的组件下,去当前模板的组件下去找到该变量。
4.作用域插槽
-
作用域插槽就是父组件对要展示的子组件的格式不满意,要拿到它的数据进行修改。
- 这就涉及1点:
- 父组件无法直接拿到子组件的变量。
#这个是父组件模板 <div id="app"> <cpn> #vue2.5x必须通过<template>的方式获取子组件里的属性。而slot-scope 则是获取子组件里面的slot <template slot-scope = "slot"> #通过slot.data拿到数据,进行处理 <span>{{slot.data.join(' - ')}}</span> </template> </cpn> </div> #这个是子组件 <template id="cpn"> <div> <slot :data="planguages"> #slot 里面的内容为默认内容,父组件里面可以进行替换 <ul> <li v-for="item in planguages">{{item}}</li> </ul> </slot> </div> </template> <script src="../js/vue.js"></script> new vue({ el:'#app', components:{ cpn:{ template:'#cpn', data(){ return { #这个是子组件里的变量,父组件无法获取 planguages:['JavaScript','C++','Java','C#','Python','Go','Swift'] } } } } })
- 这就涉及1点:
5.Webpack
- webpack 的作用就是可以模块化,和打包
- 模块化
- 在ES6之前,我们要进行模块化开发,就必须借助于其他的工具,让我们可以进行模块化开发。
- 并且在通过模块化开发完成了项目后,还需要处理模块间的各种依赖,并且将其进行整合打包。
- 而webpack其中的一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖问题(就是文件之间的导入导出)。
- 而且不仅仅是js文件,我们的css、图片、json文件等等,在webpack中都可以当做模块来使用
- 打包
- 就是将webpa中的各种资源模块进行打包合并成一个或多个包。
- 并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转换成Js等等操作。
- 模块化
5.1 具体原因和过程
因为我们写的代码可能包含,ES6,less,等的一些代码,我们需要把我们的代码上传到服务器之后然后,浏览器从服务器请求数据,这个时候这些代码浏览器是识别不了的,这个时候必须使用webpac进行转换。
5.2 webpack的使用
-
webpack的使用依赖于node的环境,node有一个包管理工具叫做npm。webpack在项目中是开发时依赖。(因为运行的时候不需要,只需要用它打包)
-
–save -dev
开发时依赖。
-
–save
运行时依赖
5.3 webpack中的其他文件安装
-
package.json文件:这个文件时webpack的一些配置信息,而这个文件是通过,在当前项目的文件夹下通过npm init 初始化自动生成。
-
webpack 的每次打包文件都需要,指定路径
#使用webpack命令行,把main.js 文件打包到bundle.js文件 webpack ./src/main.js ./dist/bundle.js
-
为了解决每次都要打包的问题,我们可以配置一些信息让他实现每次命令行不输入命令,只输入webpack 就可以实现自动打包到指定路径的效果
-
配置文件命令为webpack.config.js,配置信息入如下:
#引入path依赖,这个是node.js中的依赖 const path = require('path') module.exports = { #入口(就是需要压缩的文件的入口) entry : './src/main.js', #出口 output:{ #压缩的路径其中_dirname 是node里面的全局变量,用来找到dist这个文件位置。(这样找到得是绝对路径,而且可以动态的找到找到路径) path:path.resolve(_dirname,'dist'), #打包之后保存的文件名 filename:'bundle.js' } }
-
5.4 webpack 的全局安装和本地安装的区别:
- 当我们进入公司之后可能有一个项目正在开发,而这个项目是使用webpack3.60版本的,而我们电脑全局安装的是webpack4.2.0版本,这个时候我们如果使用全局的webpack4.20打包项目的话就会出现bug,所以我们需要在本地项目中安装一个局部的webpack3.60版本。跟项目匹配。
5.5 webpack 命令全局和本地的区别
-
只要在终端输入的命令肯定是全局的webpack,而用在项目的package.json 中的scripts:{}中定义的脚本命令是局部的,因为他会优先在本地找,找不到为在全局找。
#package.json文件 scripts:{ 'build':'webpack' } #配置好之后,可以不用输入webpack,而是通过以下方式实现。 npm run build
-
还有一种方式,当我们在局部安装好webpack之后会生成一个包,名字是node_modules,然后找到这个包里面的webpack运行webpack(这个也是本地的)。
node_modules/.bin/webpack
-
一般我们选择第一种方式运行webpack
5.6 webpack 的打包的规则
- webpack打包首先入去入口文件里面去找,这里的入口文件就是src文件夹下的main.js/或者index.js,如果文件里面有依赖的话会找到对应的依赖文件一起打包。
5.7 webpack 的loader
- webpack 只负责处理依赖和打包,像是转换ES6到ES5,或者转化其他代码,webpack 不能处理,这时候可以依赖其他扩展来处理,就是loader
5.71 css文件的loader
-
而Css文件跟入口函数没有任何依赖,我们收到把css文件引入会报错,这时候会提醒我们安装loader去处理css文件。
#main.js require(xxx.css)
-
首先我们可以去webpack官网上查找css-loader,然后命令行安装,再去wenpack.config.js中去配置相关信息。
-
但是css-loader,只负责将css文件打包,但是不负责引入到DOM中,这时候就需要用到style-loader,同样是命令行安装,配置信息。
-
有一点需要特别注意的是,我们在配置css-loader和style-loader 时,要注意一下代码
-
module:{
rules:[
{
test:/\.css$/,
#css-loader 只负责将css 文件加载
#style-loader 负责将样式添加到DOM中
#使用多个loader时,是从右到左(本来应该先css-loader,然后在style-loader,但是npm 这个执行的规则是先右后左,所以要反过来写。
ues:['style-loader','css-loader']
}
]
}
5.8 webpack处理图片文件
webpack 处理图片分为两种情况,一种是图片下雨limit限制的大小,另外是大于limit限制的大小。
-
首先我们需要在main.js入口文件中引入依赖
require('xxx.css')
然后就会提示我们安装loader,这个时候我们就可以安装url-loader,然后配置文件
modules:{ rules:[ { test:/\jpg|png|gif\jpeg/, use:[ { loader:'url-loader', options:{ #当加载的图片,小于limit时,会将图片编译成base64字符串形式。 #当加载图片,大于limit时,需要使用另外一个file-loader模块进行加载。我们只需要通过命令行安装即可。 limit:8691, #因为当大于limit时,file-loader会把图片打包,然后放到dist/文件下,这个时候我们给他一个命名规则 name:img/[name].[hash:8].[ext] } } ] } ] }
- 当图片大于limit时会出现引入的图片无法在DOM中显示,这是因为,我们的图片打包到了dist/文件下,但是路径还是在当前文件下,所以没有找到。我们只需要在配置文件的入口下面加上一段配置就行。
publicPath:'dist/'
5.9 webpack 配置babel-loader(ES6转换成ES5)
-
命令行:
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
-
配置文件
modules:{ rules:[ test:/\.js$/, #exclude 排除的意思,因为node_modules里面的文件不需要处理 exclude:/(node_modules|bower_components)/, use:{ loader:'babel-loader', options:{ presets:['es2015'] } } ] }
6.webpack 配置Vue
- 安装vue的三种方式:
- 直接下载应用
- CDN引入
- npm安装(模块化推荐方式)
6.1安装vue-loader组件
-
当我们引入vue组件时,会报错,需要我们引入vue-loader
npm install --save-dev vue-loader vue-template-compiler
-
然后配置信息
modules:{ rules:{ test:/.\vue$/, use:['vue-loader'] } }
-
需要注意的是
当我们vue安装的版本大于15.00时,会报错,需要安装别的依赖,这个时候我们在package.json里手动把vue的版本改成13.00,这个时候再 执行下面的命令就行 了。
npm install vue-loader
7.webpack的扩展 Plugin
- plugin 是webpack的一个扩展插件,而loader相当于webpack的一个转换器。
-
BannerPlugin
这个插件是用来处理,当我们打包结束之后可以在打包的文件里面看到我们定义的版权信息。
#引入webpack const webpack = require('webpack') module.exports = { ... plugins:[ new webpack.BannerPlugin('最终解释权归xxx所有') ] }
-
打包html 的 plugin插件
目前,我们的index.html文件是存放在项目的根目录下的,我们知道,在真是发布项目的时候,发布的是dist文件夹中的内容,但是dist文件夹中如果没有index.html文件,那么打包的js文件也就没有意义了。所以我们需要将index.html文件打包到dist文件夹中,这个时候就可以用HtmlWebpackPlugin插件。
-
HtmlWebpackPlugin 插件可以为我们做这些事情:
自动生成一个index.html文件(可以指定模板来生成)
将打包的js文件,自动通过script标签插入到body中。
-
-
安装HtmlWebpackPlugin插件
npm install html-webpack-plugin --save-dev
-
配置webpack.confing.js里面的内容
#导入依赖 const HtmlWebpackPlugin = require('html-webpack-plugin') #配置信息 plugins:{ new HtmlWebpackPlugin({ #这里的template表示根据什么模板来生成html文件 template:'index.html' }) }
- 另外,我们需要删除,之前在output出口中配置的publicPath属性。否则插入的script标签中的src可能会有问题。
3.js压缩的plugin
在项目发布之前,我们必然要对js等文件进行压缩处理。
- 这里我们就对打包的js文件进行压缩,我们使用一个第三方的插件,uglifyjs-webpack-plugin ,并且版本号指定1.1.1和cli2保持一致
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
-
配置信息
#引入依赖 const UgfilyJsPlugin = require('ugfily-webpack-plugin') #配置信息 modules:{ ... plugins:{ new UglifyJsPlugin() } }
8.webpack 搭建本地服务器
-
webpack提供了一个可选的本地开发服务,这个本地开发服务基于node.js搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果。
-
不过他是一个单独的模块,在webpack中使用它之前需要安装
npm install --save-dev webpack-dev-server@2.9.1
-
配置信息
- devserver 也是作为webpack中的一个选项,选项本身可以设置一下属性:
- contentBase:为哪一个文件提供本地服务,默认是根文件夹,我们这里填写’./dist’
- port:端口号
- inline:页面实时刷新
- histortyApiFallback:在SPA页面中,依赖HTML5的history模式
-
webpack.config.js文件配置的修改如下:
deServer:{ contentBase:'./dist', inline:true }
最重要的一点是:我们运行本地服务的命令:
webpack-dev-server
-
但是因为是安装在局部,所以需要找到相对路径之后才能运行。
所以我们为了简化这个命令,再配置写信息在scripts脚本里面
scripts:{
...
"dev":'webpack-dev-server --open'
#--open 表示的是直接打开浏览器,可带可不带
}
9.webpack.config.js配置文件分离
因为在webpack.config.js文件中既有,开发时依赖,也有运行时依赖,所以我们本次进行一个抽离,把开发时依赖和运行时依赖分开。
在根目录下新建一个build文件,然后在build文件夹下新建dev.config.js和base.config.js还有prod.config.js三个文件
其中base.config.js用来放置一些基础的配置,然后dev.config.js用来放置开发时依赖,而prod.config.js用来放置prod生产时依赖。
当把文件分离之后,这个时候我们需要安装一个包用来处理,我们文件的合并,因为base.config.js和dev.config.js需要在开发时合并,而base.config.js和prod.config.js需要在生产时合并。
npm install webpack-merge --save-dev
安装之后需要一下命令
#dev.config.js
const WebpackMerge = require('webpack-merge')
const BaseConfig = require('./base.config.js')
#合并设置
module.exports = WebpackMerge(BaseConfig,{
deServer:{
contentBase:'./dist',
inline:true
}
})
#prod.config.js
const WebpackMerge = require('webpack-merge')
const BaseConfig = require('./base.config.js')
const UglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin')
module.exports = WebpackMerge(BaseConfig,{
Plugin:{
new UglifyJSWebpackPlugin()
}
})
配置好之后就可以删除原有webpack.config.js文件了,然后运行命令行之后发现报错:报错原因是没有webpack.config.js文件,这个时候我们可以修改package.json文件下的scripts脚本里的命令
scripts:{
...
#因为自动找的话,只会找webpack.config.js文件,我们这里手动指定路径
"build":"webpack --config ./build/prod.config.js",
"dev":"webpack-dev-server --config ./build/dev.config.js"
}
配置好之后在运行,可以,没有错误,但是还有问题,就是打包的文件路径有问题。因为原来配置的打包的文件的路径是在webpack.config.js文件的同一级目录的dist文件夹下,现在配置文件修改到了build文件夹下了,这个时候打包的文件默认安装到build文件夹下。所以我们还得修改打包路径,在package.json文件夹下通过修改
output:{
#修改路径为 '../dist'
path:path.resolve(_dirname,'../dist')
}
10.Vue CLI 脚手架的介绍
在我们项目开发中一般不会全程手动配置webpack,我们可以通过CLI脚手架来自动配置webpack信息,来实现上述的功能。
- Vue CLI也是依赖于 node.js的,所以使用之前要安装node.js
- 同时Vue CLI也依赖于webpack ,所以要提前安装node和webpack
10.1 cnpm 安装
你可以使用淘宝定制的cnpm(gzip压缩支持)命令行工具代替默认的npm:
npm install -g cnpm --registry=https://registry.npm.taobao.org
#这样就可以使用cnpm命令来安装模块了
cnpm install [name]
10.2 Vue CLI 安装
脚手架一般都是全局安装
npm install -g @vue/cli
通过vue -version 可以查看版本,这里是3.多版本的,是不能够使用CLI2版本创建项目的。所以这个时候我们需要根据官网文档说明,通过命令行拉取2.x模板
npm install -g @vue/cli-init
10.3 通过vue CLI创建项目
vue CLI2初始化项目:
vue init webpack my-project
11.前端渲染,前端路由、后端渲染,后端路由
- 后端渲染:就是以前请求一个url的时候会向服务器发送请求,然后在服务器通过jsp代码html+css,已经通过Java跟数据库动态链接来实现整个页面的渲染,然后把渲染好的页面发送给浏览器(只发送HTML+css)。
- 后端路由:就是这种通过后端服务器来配置每个页面和url的映射关系称为后端路由。
- 前端渲染:前端渲染主要是通过url请求服务端数据,然后获取数据,通过js代码处理数据,动态的展示单浏览器页面上。
- 前端路由:就是设计SPA,单页面富应用,一般只有一个index.html文件,通过前端路由映射每个url和页面,显示数据的切换,但是页面只有一个。只是数据通过路由映射发生变化。
11.1 使用路由
-
路由的安装很简单就不说了
-
:该标签是一个vue-router 已经内置的组件,它会北渲染成一个a标签。
#点击首页跳转到对应的路径 <router-link to="/home">首页</router-link> <router-link to="/about">关于</router-link>
-
:该标签会根据当前的路径,动态的渲染出不同的组件
-
router-link 补充
-
tag 可以指定之后渲染成什么组件,比如上面的代码会被渲染成一个Li标签,而不是a标签。
<router-link tag="li to="/home"><router-link>
-
replace:不会留下history记录,所以指定replace的情况下,回退键不能返回到上一个界面中。
-
active-link-class:对应的路由匹配成功时,会给当前元素设置一个active–link-class的class,设置active-class可以修改默认名称。
const router = new VueRouter({ routes, #这个是设置url已history显示,而不是通过#hash的方式 mode:history, #这个是修改默认添加类的名字 linkActiveClass:'active-class' })
-
11.2动态路由
-
使用路由:
-
首先配置路由信息
#vue-router 的index.js const routes:[ { #动态在后面拼接字符串iid path:'./test/:iid', compontent:Test } ]
-
然后去根组件下动态配置
#App.vue <router-link v-bind:to="'/test/'+useId"><router-link> <router-view></router-view> export default{ name:"App", data(){ return { useId:lisi } } }
-
11.3 router和route
$router 是表示new VueRouter()实例化的对象,在每个页面都可以调用,也可以使用push方法和replace方法。
$route 表示的是当前哪个路由活跃就表示哪个路由。
11.4 路由的懒加载
路由的 懒加载就是我们最后打包的时候会把我们的css、js 、html代码分开打包,如果都打包到一个js 文件里,会造成我们浏览器去请求静态服务器的时候会导致文件过大,浏览器请求过程中会出现页面空白的情况。所以需要分开打包,这也就是路由的懒加载,同时打包的JS文件也会被分成三个文件和对应的每个组件的js。
-
第一个就是app.js :里面存放的是我们主要的业务代码,自己写的代码。
-
第二就是manifest.js :里面存放的是低层支撑的代码,用来处理不同文件代码之间的关系。
-
第三个就是vendor.js:里面存放的就是第三方提供的代码。vue-router,vue cli等。
-
对应的路由组件js,一般是配置的有几个路由组件,就会打包成几个js文件。
-
路由懒加载的格式是
#router 的 index.js文件 const Home = ()=> import('../components/Home')
11.5.路由嵌套
如果我们想在原有组件下,新增路由,这就需要路由嵌套
#router index.js文件夹
const routes:[
{
path:'/home',
component:Home
},
#在原来的路由下新增路由。
children:[
{ #子路径不用加/
path:'news',
component:'News'
},
{
path:'message',
component:'Message'
}
]
]
-
然后在Home.vue里面添加路由组件
#Home.vue 文件 <template> #路径格式参照下面 <router-link to="/home/news">消息</router-link> <router-link to="/home/message">通知<router-link> <router-view></router-view>
11.6传递参数
-
通过字符串传递参数:
#第一种方式通过字符串传递参数 <router-link :to="'/user/'+useId">档案</router-link> #第二种方式,通过query传递参数 <router-link :to="{path:'/profile',query:{name:'why',age:18,height:1.88}}">档案</router-link>
11.7生命周期函数
-
created() :当组件被创建好之后回调这个函数。
-
mounted() : 当template模板挂载到DOM上之后,会回调这个函数。
- 在mounted()中获取组件的offsetTop的值,根本获取不到,因为在mounted()还没有值,没有渲染。
-
updated() : 当页面发生刷新的时候会回调这个函数。
- 在updatated()中获取组件的offsetTop值,获取的值不对,this. r e f s . p a r a m s . refs.params. refs.params.el压根没有渲染。
- this.nextTick(()=>{}),获取的值也不对,原因是根据最新的数据,对应的DOM已经被渲染出来了,但是图片依然没有加载完(目前通过offsetTop获取的值是不包含图片的)
-
destroyed() :当组件发生切换的时候回到用这个函数。
-
activated() :当组件是活跃的状态时,会调用这个函数。
-
deactivated() :当组件不是活跃的状态时,会调用这个函数。
- activated 和deactivated 这两个方法,只有组件被keep-alive包含的时候才有效。
11.8 导航守卫函数
-
需求:
当我们想要在首页切换每个组件让导航栏里面的title显示每个组件对应的title时,这个时候我们可以在每个组件内定义一个生命周期函数实现
created(){ document.title = this.$route.title }
虽然这种方式可以实现,但是需要每个组件都要写入一段这个代码,比较麻烦,所以我们想到了另外一个方法,使用路由守卫函数,监听路由的变化。
-
改进代码
#router 里面的index.js文件 #beforeEach也称为前置守卫(guard) router.beforeEach((to,from,next)=>{ //从from跳转到to,meta是我们在路由中配置的参数 document.title = to.matched[0].meta.title #next()必须要加上,用来控制下一步。 next() })
const route = [
{
path:'/home',
component:Home,
meta:{
title:'首页'
}
}
]
11.8.1全局守卫、路由独享守卫、组件守卫
- 有两个,分别是前置守卫(beforEach),还有后置钩子(afterEach)
- 其中后置钩子不需要主动调用next(),内部自动调用。
- 路由独享守卫
- 组件守卫
11.9 keep-alive和router-view
-
keep-alive是vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
- keep-alive :有两个属性,分别是include,和exclude(排除)
- indclude–字符串或正则表达式,只有匹配的组件才会被缓存。
- exclude–字符串或正则表达式,任何匹配的组件都不会被缓存。
//exclude中的东西不要加空格 <keep-alive exclude="Profile,User"> <router-view/> </keep-alive>
-
router-view 也是一个组件,如果直接被包在keep-alive里面,所有的路径匹配到的视图组件也都会被缓存。
12.Promise的介绍
-
什么事promise?
Promise 是异步编程的一种解决方案。
-
那什么时候我们回来处理异步事件呢?
- 一种很常见的场景就是网络请求了。
- 我们封装一个网络请求的函数,因为不能立即拿到结果,所以不能像简单的3+4=7一样将结果返回出去。
- 所以往往我们会传入另一个函数,在数据请求成功时,将数据通过传入的函数回调出去。
- 如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦。
- 但是当网络请求非常复杂时,就会出现回调地狱。
-
简单总结一下就是(当我们进行复杂的网络请求时,如果是同步事件的话,那么相当于在请求的这段时间我们的浏览器要等到请求数据的到来,什么也不做,这会导致页面的空开,也称阻塞。这种方式非常不好,所以网络请求肯定是异步操作,然后通过传递参数,在请求成功时,通过回调把数据回调出来。
12.1 Promise的第一种写法
-
new —> 构造函数(1.保存一些状态信息,2.执行传入的函数)。
-
在执行传入的回调函数时,会传入两个参数,resolve,reject本身又是函数。
-
处理的函数一般都写在then和catch方法里面,这样结构清晰,便于维护。而resolve和reject只是取到数据,不进行处理。
new Promime((resolve,reject)=> { setTimeout(()=>{ //成功的时候调用 resolve //resolve('Hello World') //失败的时候调用reject. reject('Error') },1000) }).then((data)=>{ console.log(data) console.log(data) console.log(data) console.log(data) console.log(data) }).catch(err=>{ console.log(err) })
12.2 Promise的第二种写法
new Promise((resolve,reject)=>{
setTimeout(()=>{
//成功的信息
resolve('Hello Vue.js')
//返回错误信息
reject('Error message')
},1000)
//then(函数1,函数2),函数1用来处理成功,函数2用来处理失败
}).then(data=>{
console.log(data)
},err=>{
console.log(err)
})
12.3 Promise 的链式调用
- 第一种链式调用的书写方式
new Promise((resolve,reject)=>{
#第一次网络请求
setTimeout(()=>{
resolve('Hello Vue.js')
},1000)
}).then(res=>{
#处理拿到的数据
console.log(res)
console.log(res)
console.log(res)
return new Promise((resolve,reject)=>{
#第二次网络请求
setTimeout(()=>{
resolve('Hellow Python')
},1000)
})
}).then(res=>{
console.log(res)
console.log(res)
console.log(res)
return new Promise((resolve,reject)=>{
#第三次网络请求
setTimeout(()={
resolve('Hellow JS')
},1000)
})
}).then(res=>{
console.log(res)
console.log(res)
console.log(res)
})
2.第二种链式调用方式:
new Promise(resolve=>{
resolve(res+'111')
})的简写:Promise.resolve(res+'111')
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('aaa')
},1000)
}).then(res=>{
console.log(res,'第一层处理的10行代码')
//对结果进行第一次处理
return Promise.resolve(res+'111')
}).then(res=>{
console.log(res,'第二层的10行处理代码')
//对结果进行第二次处理
return Promise.resolve(res+'222')
}).then(res=>{
console.log(res,'第三层的10行处理代码')
})
3.链式调用的第三种写法:
return Promise.resolve(res+'111')的简写
return res+'111'
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('aaa')
},1000)
}).then(res=>{
console.log(res,'第一层处理的10行代码')
//对结果进行第一次处理 简写的方式
return res+'111'
}).then(res=>{
console.log(res,'第二层的10行处理代码')
//对结果进行第二次处理,简写的方式
return res+'222'
}).then(res=>{
console.log(res,'第三层的10行处理代码')
})
12.4 Promise 的all方法
当我们的项目中要请求到数据,必须在对两个url进行都完成时才能够取到数据,这个时候就可以使用Promise.all([])方法,里面必须是可迭代对象interator,也就是可以遍历的。
Promise.all([
#网络请求1
new Promise((resolve,reject=>{
setTimeout(()=>{
resolve({name:'why',age:18})
},1000)
}),
#网络请求2
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve({name:kobe,age:19})
},2000)
})
]).then(results=>{
#results,取到的是一个数组,包含上面两个网络请求的数据
console.log(results)
})
13.axios 网络请求
-
axios(config):
axios({ url:'https://www.baidu.com' }).then(res=>{ console.log(res) })
-
axios(config):字符串拼接的模式
axios({ url:'http://123.207.32.32:8000/home/data', //专门针对get请求的参数拼接 params:{ type:'pop', page:1 } }).then(res=>{ console.log(res) })
13.1 使用axios发送并发请求
- 使用axios.all可以放入多个请求的数组。
- axios.all([ ]) 返回的结果是一个数组,使用axios.spread 可将数组[res1,res2]展开为res1,res2。
axios.all([axios.get('http://123.207.32.32:8000/category'),
axios.get('http://123.207.32.32:8000/home/data',{
params:{type:'sell',page:1}})
]).then(axios.spread((res1,res2)=>{
console.log(res1);
console.log(res2)
}))
13.2 使用第三方框架的注意点
- 当我们引用第三方框架的时候,切记不能让每个vue的文件都对第三方框架产生依赖,也就是每个vue文件都导入第三方的框架,这样如果有一天这个第三方框架不能用了就会导致,我们每个vue文件都要进行修改。
13.3 axios 的全局配置
axios.defaults.baseURL = 'http://123.207.32.32:8000'
axios.defaults.timeoute = 5000
- 全局配置有缺点,就是当我们对一个服务器请求的人数很多时这时候服务器可能会受不了。
- 所以我们后期会使用nginx分布式部署,就是有多个服务器,然后当有很多人请求时,我们会通过nginx分布式把请求分散到多个服务器上,减轻单个服务器的请求压力。
- 所以这个时候多个服务器的baseURL地址不同,我们不能使用全局配置。这个时候就有个 axios实例的方式,更合适。
13.4 axios 的实例对象
const instance = axios.create({
baseURL = 'http://123.207.32.32:8000',
timeout = 5000
})
instance({
url:'/home/multidata'
}).then(res=>{
console.log(res)
})
instance({
url:'/home/data'
}).then(res=>{
console.log(res)
})
13.5 封装axios 函数文件 —>传入三个参数
单独封装这个函数文件的作用就是让不同的vue组件面对这个函数文件,然后这个函数文件依赖第三方框架axios,如果有一天axios这个框架不使用了只需要改变这个函数文件就可以了。
#src 文件夹下新建network文件夹,下新建requeset.js
import axios from 'axios'
export function request(config,success,failure){
//创建实例对象
const instance = axios.create({
baseURL = 'http://123.207.32.32:8000',
timeout = 5000
})
//发送真正的网络请求
instance(config).then(res=>{
#这个地方不能对res 进行处理,因为这个地方只能请求到数据,具体的数据处理应该交给具体的页面组件中
#所以需要把res 这个数据回调出去
console.log(res);
success(res)
})
}
#main.js页面
import {request} from '.network/request'
#传递了三个参数,分别是config,success,failure,通过函数回到res,err数据,进行处理。
request({url:'/home/data'},res=>{
console.log(res);},err=>{console.log(err)})
13.6 封装axios函数文件 -->传入一个参数
#src 文件夹下新建network文件夹,下新建requeset.js
import axios from 'axios'
export function request(config){
//创建实例对象
const instance = axios.create({
baseURL = 'http://123.207.32.32:8000',
timeout = 5000
})
//发送真正的网络请求
//从config中取出baseConfig
instance(config.baseConfig)
.then(res=>{
#这个地方不能对res 进行处理,因为这个地方只能请求到数据,具体的数据处理应该交给具体的页面组件中
#所以需要把res 这个数据回调出去
//从config中取出success
config.success(res)
}).catch(err=>{
//从config中取出failure
config.failure(err)
})
}
#main.js页面
import {request} from '.network/request'
#传递了一个参数config,通过函数回到res,err数据,进行处理。
config包含三个部分,分别是baseConfig、success、failure
request(
baseConfig:{
},
success:function(res){
}
failure:function(err){
}
13.7 通过Promise的函数封装
#src 文件夹下新建network文件夹,下新建requeset.js
import axios from 'axios'
export function request(config){
return new promise((resolve,reject)=>{
//1.创建axios实例
const instance = axios.create({
baseURL = 'http://123.207.32.32:8000',
timeout:5000
})
//2.发送真正的网络请求
instance(config).then(res=>{
resolve(res)
}).catch(err=>{
reject(err)
})
})
}
#main.js页面
import {request} from '.network/request'
request({
url:'/home/multidata'
}).then(res=>{
console.log(res)
}).catch(err=>{
console.log(err)
})
13.8 最终版封装axios函数
#src 文件夹下新建network文件夹,下新建requeset.js
import axios from 'axios'
export function request(config){
//1.创建axios实例
const instance = axios.create({
baseURL : 'http://123.207.32.32:8000',
timeout:5000
})
//2.发送真正的网络请求
//因为instance 这个方法在axios的源码中最终调用是会变成instancePromise的函数,所以最后我们直接返回这个函数就行。
return instance(config)
}
13.9 axios 拦截器的作用
- 请求拦截的作用
#interceptors是拦截器的意思。
#request需要上传两个参数,两个函数,一个是满足的函数,一个是不满足的函数
instance.interceptors.request.use(
#第一个函数参数
config=>{
console.log(config);
#拦截之后必须要返回,否则之后请求无法成功。
return config
},
#第二个函数参数
err=>{
console.log(err)
})
//拦截器的使用场景:
//1.比如config中的一些信息不符合服务器的要求。
//2.比如每次发送网络请求时,都希望在页面中显示一个请求的图标。
//3.某些网络请求(比如登录token),必须携带一些特殊的信息。
2.响应拦截的作用
instance.interceptors.response.use(
res=>{
cocnsole.log(res);
#可以对res结果进行处理。
return res.data
},
err=>{
console.log(err);
}
)
14 项目开发
14.1 better-scroll 的使用
better-scroll 因为第三方的安装包,所以一定要新建一个vue文件进行封装,然后谁使用它,谁就单独面对这个文件就行了。
-
首先安装better-scroll
npm isntall better-scroll --save
-
better-scroll的结构
<div class="wrapper"> <div class="content"> ... </div> </div>
然后给wrapper这个div一个高度。
-
导入better-scroll
import BScroll from "better-scroll"
-
创建实例对象
const bscroll = new BScroll(.wrapper,{ //用来侦测是否对滚动金西行侦测,默认不侦测数值是0,1也是不侦测,2是侦测。但是只侦测手指拖动时的滚动,手指离开时的惯性滚动不侦测。3是只要是滚动都侦测。 probeType:0, //阻止原生的时间,默认是false click:true, //阻止上来加载更多。默认是false pullUpLoad:true })
-
如果要使用上拉加载更多,首先得pullUpLoad:true
-
然后必须监听pullingUp事件
bscroll.on('pullingUp',()=>{ console.log('上拉加载更多'); //发送网络请求,加载更多数据 //但是pullingUp只能加载一次,要加载一下次,必须添加,finishPullUp bscroll.finishPullUp() })
-
-
监听实时位置
//使用事件scroll bscroll.on('scroll',position=>{ console.log(position) })
14.2 refs
this.$refs.aaa 拿到的是组件对象 aaa 是在组件里面
this.$refs.bbb 拿到的是元素对象 bbb是在元素里面
14.3 无法滚动的bug处理
-
无法滚动的原因是图片是异步加载,而scroll 判断高度的时候图片还没有加载出来所以可滚动的高度是100+,但是等图片加载出来是,不会重新计算高度,导致无法向下滚动。
-
解决方法就是对goods-item组件里面的图片进行监听,当有一张图片完成加载就执行一次refresh。重新计算高度。
-
因为goods-item是home组件下的孙组件,所以通信比较麻烦,建议使用vuex或者时间总线处理。
- vuex 是用来保存公共组件的状态
- 事件组件是用来保存组件事件的。
-
使用事件总线
//首先正在main.js中建立事件总线 Vue.prototyep.$bus = new Vue() //然后在goods-item.vue组件里面监听图片加载 imgLoad(){ this.$bus.$emit('itemImgLoad') } //然后在home.vue组件里监听,在DOM挂载完成后 mounted(){ this.$bus.on('ItemImgLoad',()=>{ //调用scroll组件里面的refresh方法 this.$refs.scroll.refresh() }) }
14.4 防抖函数的使用
-
当解决页面卡顿使用refresh时,就要检测图片的加载,当有一个图片加载就refresh一次,每一次刷新页面就要请求服务器,所以这种方式,对服务器压力比较大。
-
所以我们可以使用防抖函数,举个例子,我们可以控制时间,假设是1秒当在1秒的时间内,我们有多次刷新,这个时候就只会调用一次。
-
函数的封装
//调用debounch函数 refersh是debounch的函数返回值(也是一个函数) const refresh = debounce(this.$refs.scroll.refresh,500) mounted(){ this.$bus.$on('itemImgLoad',()=>{ refresh() }) } //对debounch函数的封装 debounch(func,delay){ //这个let timer = null 一定要写在外面要不然下次就会被清除。 let timer = null return function(...argus){ if(timer) clearTimeOut(timer); timer = setTimeOut(()=>{ //这里的func 相当于this.$refs.scroll.refresh func.apply(this,argus) },delay) } }
14.5 保持home 首页的内容
-
首先在app.js中使用keep-alive
<keep-alive> <router-view/> </keep-alive>
-
然后通过activate(){}生命周期函数跳转到保持的位置
activated(){ this.$refs.scroll.scrollTo(0,this.saveY,500) //必须要刷新一下,不刷新可能导致页面无法滚动 this.$refs.scroll.refresh() }
-
通过deactivated(){}生命周期函数获取当前位置的值
deactivated(){ this.saveY = this.$refs.scroll.getSaveY() }
-
其中 saveY 是声明在data中的变量。
data(){ return { saveY:0 } }
14.6 正则表达式
- 基本语法:
- +:表示匹配一个或者多个。
- *:表示匹配0个或者多个。
- ?:表示匹配0个或者1个。
- RegExp.$1:表示的是匹配到的第一个对象。
14.7 类的继承
-
继承是发生在类的里面,可以减少重复的代码
class Animal { run(){ console.log('这是一个run方法') } } //Person 这个类继承Animal的run方法 class Person extends Animal { run(){ console.log('这是一个run方法') } }
14.8 两个组件之间共用相同的代码使用混入mixin
相同的内容:包括方法,mounted,components,data中的变量等都可以放到混入中
使用的方法
-
先在src /common目录下,新建mixin.js文件夹。
-
写入代码
-
import {debounce from './utils.js'} export const itemListenerMixin = { mouted(){ let refresh = debounce(this.$refs.scroll.refresh,500) //itemImgListener 是声明在home 和detail组件下的变量 this.itemImgListener = ()=>{refresh()} this.$bus.$on('itemImgLoad',this.itemImgListener) } }
-
在home和detail组件中导入
import {itemListenerMixin} from '../../common/mixin.js'
5.在home和detail组件的option中加入mixins:[]
export default {
name:'Home',
mixins:[ itemListenerMixin ]
}
14.9 联动效果–detail页面的导航栏
-
首先获取导航栏的index索引
detail 的导航栏组件 <div @click="changeNav(index)"></div> methodes:{ changeNav(index){ this.$emit('changeNav',index) } } detail 的页面 <nav-item @changeNav="scrollPosition"></nav-item> data(){ return { scrollY:[], getOffsetTop:null } }, created(){ this.getOffsetTop(()=>{ this.scrollY = [] this.scrollY.push(this.$refs.shangpin.$el.offsetTop) this.scrollY.push(this.$refs.canshu.$el.offsetTop) this.scrollY.push(this.$refs.pingjia.$el.offsetTop) this.scrollY.push(this.$refs.tuijian.$el.offsetTop) },50) }, methods:{ scrollPosition(index){ this.$refs.scroll.scrollTo(0,-this.scrollY[index],100) }, detailImage(){ this.getOffsetTop() } }
15. 易错点分析
-
for 循环的问题
//1.使用for in 循环打印出来的i 是字符串 for(let i in item){ console.log(i) //i是字符串。需要转换成数字格式 } //2.使用for 循环 打印出来的i是数字 for(let i = 0;i<item.length;i++){ console.log(i)//i是数字 }
16.vux 的使用
17.toast 插件方式的封装
-
一种是普通方式的封装
- 创建vue组件,Toast.vue
- 然后在每个页面中导入使用
-
第二种是通过插件的方式封装组件
-
在src文件夹下,新建toast文件夹,然后新建index.js文件和Toast.vue组件。
-
然后在main.js中导入
-
最后在index .js中创建组件构造器,和把组件挂载到div上
-
然后在toast.vue中添加具体的方法
this.$toast.show()//可以实现全局调用
-
18.fastclick 解决移动端点击延迟300ms的问题
使用方法
-
安装fastclick
npm install fastclick --save
-
导入fastclick
import FastClick from 'fastclick'
-
使用attach方法
FastClick.attch(document.body);
19.图片懒加载
使用步骤
-
下载
npm install vue-lazyload --save
-
导入
import VueLazyLoad from 'vue-lazyload'
-
使用
Vue.use(VueLazyLoad,{ loading:require('....') //loading是用来自定义未加载出来图片时候的占位图 })
-
修改图片的路径
<img :src="..."> //修改为 <img v-lazy="...">