B站 - Vue 学习笔记
0 课程介绍
- 邂逅Vue.js
- Vue基础语法
- 组件化开发
- Vue CLI详解
- vue-router
- vuex详解
- 网络封装
- 项目实战
1 邂逅Vue.js
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的 渐进式框架。
1.1 MVVM架构
MVVM架构
MVC与MVP
MVVM
Vue.js的定位
Vue.js在MVVM架构中的定位
Vue.js的功能定位
Vue.js的核心思想与特点
Reactive Components for Modern Web Interfaces(数据驱动的组件,为现代的 Web 界面而生)
- 数据驱动
- 组件化
- 特点:侵入性低、鼓励模块化、轻量、高性能
数据驱动的 Web 开发
Vue.js 安装
- 下载Vue.js,通过
- NPM安装
- vue-cli:配套路由等灵活的命令行框架
传统视图驱动 Web 开发:通过原生js/jquery操作DOM元素
数据驱动的 Web 开发:将Vue实例中数据和方法等,通过Vue指令的方式绑定到html中
1.2 Vue初体验
HelloVue.html
TestVue {{message}}Vue列表展示
Vue列表展示 - {{item}}
Vue案例-计数器
计数器 当前计数:{{counter}}
1.3 Vue生命周期
在github中下载vue源码(tag2.6.14)后,用vscode打开src --> core --> index.js,发现import Vue from './instance/index'
打开./instance/index后就可以看到Vue函数,==options表示new Vue({})传入的el、data、methods==
import { initMixin } from './init'
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
进入./init后,发现生命周期
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
Vue官网生命周期流程
理解Vue生命周期
2 Vue基础语法
vscode前端代码规范缩进设置为2
2.1 Mustache语法
- Mustache中可以写简单的表达式,如果是复杂的表达式推荐使用computed计算属性。==Mustache也是响应式的==。Mustache作用在显示内容本身。
{{message}}
{{firstName + lastName}}
{{firstName + ' ' + lastName}}
{{firstName}} {{lastName}}
{{counter*2}}
2.2 v-once语法
- ==v-once是非响应式的==,不希望随意更改数据或界面。v-once作用在标签上。
{{message}}
{{message}}
2.3 v-html语法
- v-html用来按照html格式进行解析显示,v-html作用在标签上。
- 历史遗留问题: {{{}}}以前可以使用三大括号来处理html格式解析,现在不再支持了。
2.4 v-text语法
- v-text与Mustache比较相似,但是v-text作用在标签上,并且会覆盖掉原来的内容。==不推荐使用==
{{message}}, zfcer
, zfcer
2.5 v-pre语法
- v-pre用于跳过这个元素和它子元素的编译过程,用于==显示原本的Mustache语法==。
{{message}}
{{message}}
2.6 v-cloak语法
- 当Vue加载前,v-cloak存在;当Vue加载后,v-cloak被移除。
v-cloak语法 {{message}}
2.7 v-bind语法
- v-bind基本用法:==作用于标签属性上==,语法糖
:
v-bind动态绑定class对象:通过v-bind:class绑定标签上的class属性
- 在v-bind:class属性中传入对象{style1: boolean1, style2: boolean2},可以做到==自动根据boolean来进行拼接==
- 在普通class上定义公共style,最终会实现将普通class与v-bind:class进行拼接
- v-bind:class也可以通过函数的方式传入对象
v-bind动态绑定class(对象语法) Hello Vue (对象)
Hello Vue (对象)
- v-bind绑定class数组:与v-bind绑定class对象类似
v-bind动态绑定class(数组语法) - 不推荐 Hello Vue (数组)
Hello Vue (数组)
- 练习:v-for与v-bind结合
作业 - v-for和v-bind - {{index}} - {{m}}
- {{index}} - {{m}}
- v-bind动态绑定style对象:使用驼峰命名
v-bind动态绑定style(对象语法) Hello Vue (对象)
Hello Vue (对象)
- v-bind动态绑定style数组
v-bind动态绑定style(数组语法) Hello Vue (对象)
Hello Vue (对象)
2.8 computed属性
- computed基本语法
{{firstName +' '+ lastName}}
{{firstName}} {{lastName}}
{{getFullName()}}
{{fullName}}
- computed复杂操作
总价格为:{{totalPrice}}
- computed的setter和getter
{{fullName}}
- computed与methods属性对比:computed有缓存,methods没有缓存
{{getFullName()}}
{{getFullName()}}
{{getFullName()}}
{{getFullName()}}
{{getFullName()}}
{{getFullName()}}
{{fullName}}
{{fullName}}
{{fullName}}
{{fullName}}
{{fullName}}
{{fullName}}
2.9 v-on语法
- v-on时间监听基本语法:==在事件监听时,如果方法没有参数,可以省略(); 但是在Mustache语法中是不能省略的==。v-on语法糖
@
当前计数:{{counter}}
- v-on时间监听参数问题:浏览器参数可以通过$event获得;当函数只需要event一个参数时,可以通过省略()的方式获得浏览器产生的event
v-on事件监听修饰符
- .stop - 调用 event.stopPropagation()。
- .prevent - 调用 event.preventDefault()。
- .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
- .native - 监听组件根元素的原生事件。
- .once - 只触发一次回调。
aaaaa
2.10 v-if/v-else-if/v-else语法
- v-if与v-else使用:==v-if与v-else作用在标签上==
aaa bbb ccc {{message}}
isShow为false,不显示...
- v-else-if
优秀
良好
及格
不及格
成绩表现:{{result}}
练习:用户登录方式切换。
- 注意:==由于虚拟DOM的存在,导致两个input控件会复用。==这里需要特别使用key来做区分,保证登录方式切换后,input不被复用(密码清空)
v-if与v-show的区别
- v-if是真正把标签元素移除了、
- v-show只是加了个display: none
注意:==当元素在显示与隐藏之间频繁切换时,推荐使用v-show当元素就是一次切换,推荐使用v-if==
{{message}}
{{message}}
2.11 v-for语法
- v-for遍历数组&对象
- {{item}}
- {{index}} -> {{item}}
- {{value}}
- {{key}} -> {{value}}
- {{index}} -> {{key}} -> {{value}}
- v-for中绑定key:绑定唯一的key后让v-for操作更加高效---DIff算法
- {{item}}
2.12 数组的响应式操作
数组响应式函数:push、pop、shift、unshift、splice、sort、reverse
- 注意:==直接根据数组下标修改数组元素不是响应式的==
- 非响应式操作改进手段:用splice/set代替
- {{item}}
2.13 书籍购物车案例
- style.css
table { border: 1px solid #e9e9e9; border-collapse: collapse; border-spacing: 0;}th,td { padding: 8px, 16px; border: 1px solid #e9e9e9; text-align: left;}th { background-color: #f7f7f7; color: #5c6b77; font-weight: 600;}
- main.js:==过滤器属性filters==
const app = new Vue({ el: '#app', data: { books: [ { id: 1, name: 'java study', date: '2021-7-15', count: 1, price: 127.00 }, { id: 2, name: 'java study', date: '2021-7-15', count: 1, price: 100.00 }, { id: 3, name: 'java study', date: '2021-7-15', count: 2, price: 152.00 }, { id: 4, name: 'java study', date: '2021-7-15', count: 3, price: 59.00 } ] }, computed: { totalPrice(){ let allPrices = 0; // for循环的三种写法 // for(let book of this.books) // allPrices += book.price * book.count; // for(let i = 0; i preValue + book.price * book.count, 0) return allPrices; } }, methods: { showFixedPrice(price){ return '¥'+price.toFixed(2); //toFixed(2)保留两位小数 }, decrement(index){ if(this.books[index].count == 1) return; this.books[index].count--; }, increment(index){ this.books[index].count++; }, removeBook(index){ this.books.splice(index, 1); } }, // 过滤器:注意它的使用 {{value | showPrice}} filters: { showPrice(price){ return '¥'+price.toFixed(2); //toFixed(2)保留两位小数 } }})
- index.html
书籍购物车案例 编号 书籍名 出版日期 价格 数量 操作 {{book.id}} {{book.name}} {{book.date}} {{book.price | showPrice}} {{book.count}}
总价格为:{{totalPrice | showPrice}}
购物车为空
2.14 v-model语法
- 表单双向绑定
{{message}}
- v-model原理:==v-bind 与 v-on==
{{message}}
- v-model与radio结合使用
{{sex}}
- v-model与checkbox结合使用
{{isAgree}}
篮球 足球 乒乓球 羽毛球 {{hobbits}}
- v-model与select结合使用
{{fruit}}
{{fruits}}
- v-model与v-bind值绑定:就是动态的给value赋值,如果不使用v-bind:value就会导致value=“ f ”值就是f字符串,而不是fruit
{{fruits}}
v-model修饰符的使用
- lazy懒加载
- number修饰符
- trim修饰符
{{message}}
{{age +' age的类型为:'+ typeof(age)}}
{{message}}
3 Vue组件化开发
组件化是Vue.js中的重要思想:组件树
3.1 组件化基本使用
组件化使用步骤:1 创建组件构造器对象; 2 注册组件; 3 使用组件
3.2 全局组件&局部组件
3.3 父组件与子组件
3.4 组件注册语法糖
- 将Vue.extend({})省略掉,直接在注册组件时传入template
3.5 组件模板的分离写法
- 推荐使用模板分离template的方式进行模板分离
我是标题
我是内容
3.6 组件中数据存放问题
- 组件中数据使用函数方式存放:避免多个组件实例之间数据共享导致混乱
当前计数为:{{counter}}
3.7 父子组件通信
- 父传子(props)
- {{hobbit}}
{{cmessage}}
- 父传子的驼峰标识问题:v-bind:props属性时,必须把props属性改为驼峰命名
{{cInfo}}
子传父
- 子组件emit发射出数据
- 子组件标签中通过属性将数据传递给父组件
{{result}}
- 父子通信案例:通过props和emit实现版
cnum1: {{cnum1}}
dnum1: {{dnum1}}
cnum2: {{cnum2}}
dnum2: {{dnum2}}
- 父子通信案例:通过watch实现版
cnum1: {{cnum1}}
dnum1: {{dnum1}}
cnum2: {{cnum2}}
dnum2: {{dnum2}}
3.8 组件访问
父访问子($childern、\$refs)
- 在组件标签上设置ref属性名后,就可以通过$childern、\$refs完成对子组件数据的访问。 ==推荐使用$refs==。
这是组件
- 子访问父($parent、\$root)
这是cpn组件
这是ccpn组件
3.9 slot插槽
- slot基本使用
haha hehe 插槽标签1
插槽标签2 组件标题
组件内容
- slot具名插槽
haha left center right
作用域插槽
- 编译的作用域:==父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译==
- 作用域插槽的使用:1 template标签上绑定数据languages; 2 组件标签上使用slot-scope获取slotProps
{{slot.languages.join(' * ')}} - {{language}}
4 Vue CLI详解
4.1 ES6模块化实现
- 导入方式
// 1.导入的{}中定义的变量import {flag, sum} from "./aaa.js";if (flag) { console.log('小明是天才, 哈哈哈'); console.log(sum(20, 30));}// 2.直接导入export定义的变量import {num1, height} from "./aaa.js";console.log(num1);console.log(height);// 3.导入 export的function/classimport {mul, Person} from "./aaa.js";console.log(mul(30, 50));const p = new Person();p.run()// 4.导入 export default中的内容, 可以省略{}import defaultname from "./aaa.js";defaultname('你好啊');// 5.统一全部导入// import {flag, num, num1, height, Person, mul, sum} from "./aaa.js";import * as aaa from './aaa.js'console.log(aaa.flag);console.log(aaa.height);
- 导出方式
var name = '小明'var age = 18var flag = truefunction sum(num1, num2) { return num1 + num2}if (flag) { console.log(sum(20, 30));}// 1.导出方式一:export { flag, sum}// 2.导出方式二:export var num1 = 1000;export var height = 1.88// 3.导出函数/类export function mul(num1, num2) { return num1 * num2}export class Person { run() { console.log('在奔跑'); }}// 4.export default// 一个模块内只能有一个export default,不然导出的时候就乱套了const address = '北京市'export default addressexport default function (argument) { console.log(argument);}
4.2 webpack使用
webpack起步
安装webpack,需要node.js的环境
- node -v #查看node版本
- npm install [email protected] -g #全局安装webpack3.6.0
- npm install [email protected] --save-dev #局部安装webpack3.6.0
- 注意:在终端直接执行webpack命令,使用的全局安装的webpack;当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack。
- webpack打包指令:
webpack src/main.js dist/bundle.js
//1. webpack导入方式const {add, mul} = require('./mathUtils.js')//2. webpack导出方式function add(num1, num2){ return num1+num2}function mul(num1, num2){ return num1*num2}// webpack方式导出module.exports = { add, mul}
webpack配置
通过
npm init
生成package.json
文件, 并配置脚本{ "name": "meetwebpack", "version": "1.0.0", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" }, "author": "", "license": "ISC", "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-es2015": "^6.24.1", "css-loader": "^2.0.2", "file-loader": "^3.0.1", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2", "webpack": "^3.6.0" }, "description": ""}
创建
webpack.config.js
文件, 配置入口、出口// 这里导入的path是node.js中的path: 通过npm init获取node中的path环境const path = require('path')module.exports = { entry: './src/main.js', output: { // __dirname的下划线是两个 path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' }}// 通过npm install [email protected] --save-dev 安装局部开发webpack,// 最后会生成package.json、package-lock.json文件
webpack的loader:在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。给webpack扩展对应的loader就可以实现上述的功能。
- loader使用过程:1 npm安装对应的loader(loader可以在webpack官网上找到);2在webpack.config.js中的modules关键字下进行配置;
- 安装的loader:css-loader、style-loader、less-loader、url-loader、file-loader、ES6语法处理loader(babel-loader、babel-core、babel-preset-es2015)
//package.json中的devDependencies"devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-preset-es2015": "^6.24.1", "css-loader": "^2.0.2", "file-loader": "^3.0.1", "less": "^3.9.0", "less-loader": "^4.1.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2", "webpack": "^3.6.0" }, // webpack.config.js中的moudlemodule: { rules: [ { test: /\.css$/i, // css-loader只负责将css文件进行加载 // style-loader负责将样式添加到DOM中 // 使用多个loader时,是从右往左加载 -- 注意先后顺序 use: ["style-loader", "css-loader"], }, { test: /\.less$/i, loader: [ // compiles Less to CSS "style-loader", "css-loader", "less-loader", ], }, { test: /\.(png|jpg|gif)$/i, use: [ { loader: 'url-loader', options: { // 当加载的图片,小于limit时,会将图片编译为base64字符串形式 -- 不需要打包到dist中 // 当加载的图片,大于limit时,需要使用file-loader模块进行加载 -- 需要打包到dist中 limit: 5632, // 人为设置打包到dist文件夹下的图片路径, hash:8标识截取hash的前8位做图片名;ext是图片后缀 name: 'img/[name].[hash:8].[ext]' }, }, ], }, { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { // 利用babel将es6转为es5, // 安装命令如下:npm install --save-dev [email protected] [email protected] [email protected] loader: 'babel-loader', options: { presets: ['es2015'] } } } ],}, //main.js中导入css、less// 导入base.css文件require('./css/base.css')// 导入.less文件require('./css/special.less')
webpack配置vue
- 安装vue:
npm install vue --save
runtime-only问题:在webpack.config.js中配置
// 添加vue:runtime-compiler环境resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' // 用 webpack 时需用'vue/dist/vue.common.js' }}
- .vue文件封装处理::安装vue-loader、vue-template-compiler
- 安装vue:
webpack配置plugin:loader主要用于转换某些类型的模块,它是一个转换器;plugin是插件,它是对webpack本身的扩展,是一个扩展器。
- plugin的使用过程:1通过npm安装需要使用的plugins;2在webpack.config.js中的plugins中配置插件
//webpack.config.js// 引用插件plugins: [ new HtmlWebpackPlugin({ template: 'index.html' //为了在dist index.html中配置index.html中的div#app }), new UglifyJsPlugin(), //压缩dist中的bundle.js new webpack.BannerPlugin('最终版权归zfcer所有'),]
webpack-server配置
- 安装webpack-server:
npm install --save-dev [email protected]
在webpack.config.js中配置
devServer: { contentBase: './dist', inline: true //inline表示页面实时刷新}
- 安装webpack-server:
配置分离
- 配置base.config.js,在dev.config.js和pro.config.js中导入基本配置
在package.json中使用配置
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config ./build/pro.config.js", "dev": "webpack-dev-server --open --config ./build/dev.config.js"},
4.3 Vue CLI2
- 安装Vue CLI:
npm install -g @vue/[email protected]
、npm install -g @vue/cli-init
按照Vue CLI2的方式初始化项目 初始化项目:
vue init webpack my-project
目录结构
Runtime-Compiler和Runtime-only的区别
Vue程序运行过程: template --> ast --> render --> VDOM --> UI
当我们在使用.vue文件时,==.vue中的template是由vue-template-compiler来处理的==,所以可以直接使用render来渲染。runtime only ,比runtime compiler少6k空间 (效率更高,空间更小)。
npm run build
npm run dev
webpack.base.conf.js起别名
4.4 Vue CLI3
Vue CLI3 与 Vue CLI2区别
- vue-cli 3 是基于 webpack 4 打造,vue-cli 2 还是 webapck 3
- vue-cli 3 的设计原则是“0配置”,移除的配置文件根目录下的,build和config等目录
- vue-cli 3 提供了 vue ui 命令,提供了可视化配置,更加人性化
- 移除了static文件夹,新增了public文件夹,并且index.html移动到public中
创建Vue CLI3项目:
npm create my-project
目录结构
在
vue ui
上查看配置:一大堆配置文件被放在node_modules/@vue/cli-service/lib/Service.js文件下起别名,在项目目录下创建
vue.config.js
文件
5 vue-router
5.1 vue-router的使用
后端路由&前端路由
- 后端路由:早期的网站开发整个HTML页面是由服务器来渲染的(JSP)。当我们页面中需要请求不同的路径内容时, 交给服务器来进行处理, 服务器渲染好整个页面, 并且将页面返回给客户端.p这种情况下渲染好的页面, 不需要单独加载任何的js和css, 可以直接交给浏览器展示, 这样也有利于SEO的优化.
前端路由:
- 前后端分离阶段:后端只提供API来返回数据, 前端通过Ajax获取数据, 并且可以通过JavaScript将数据渲染到页面中。(根据Ajax请求从静态服务器中获取对应的静态资源)
- 单页面富应用SPA阶段:SPA最主要的特点就是在前后端分离的基础上加 了一层前端路由。(根据axios路由请求一次性把所有静态资源获取(路由lazy加载))
URL变更
- location.href来修改url
- history.pushState({}, '', '/foo')相当于压栈,在浏览器中可以点击返回按钮
- history.replaceState({}, '', '/foo'), 在浏览器中不能点击返回按钮
- history.go(-1)弹栈一个元素,等价于history.back() (并不是真正弹栈)
- history.forward() 则等价于 history.go(1)
- 安装vue-router:
npm install vue-router --save
使用vue-router:1创建路由组件;2配置路由映射:组件与路径映射关系;3使用路由:通过
和 - 路由嵌套:children: [ ]
- 参数传递:动态url,'/user/:userId'传递参数userId。在组件中通过
this.$route.params.userId
来获取 - 导航守卫:通过在beforeEach中利用to来获取meta来传递导航栏标题, 通过
to.matched[0].meta.title
来获取meta参数 kepp-alive:keep-alive包裹router-view设置访问缓存, 这样可以让组件created只执行一次(缓存)。组件内的 activated 和 deactived 生命周期函数必须在设置keep-alive标签的包裹下来生效
: 该标签是一个vue-router中已经内置的组件, 它会被渲染成一个标签. : 该标签会根据当前的路径, 动态渲染出不同的组件.
//index.jsimport Vue from 'vue'import Router from 'vue-router'// 路由懒加载: 不要直接注册组件const Home = () => import('@/components/Home')const HomeMessages = () => import('@/components/HomeMessages')const HomeNews = () => import('@/components/HomeNews')const About = () => import('@/components/About')const User = () => import('@/components/User')const Profile = () => import('@/components/Profile')// 1. 通过Vue.use(插件),安装插件Vue.use(Router)// 源码分析:Vue.use(Router)的内部操作是install(Vue类), 在install内部全局注册了RouterView 和 RouterLinkconst routes = [ { path: '/', redirect: '/home', meta: { title: '首页' }, }, { path: '/home', component: Home, meta: { title: '首页' }, children: [ { path: '', component: HomeNews }, { path: 'news', //children中的path一定不能加'/', routes中的path可以加'/' component: HomeNews }, { path: 'messages', component: HomeMessages } ] }, { path: '/about', component: About, meta: { title: '关于' }, }, { path: '/user/:userId', //注册动态url component: User, meta: { title: '用户' }, }, { path: '/profile', component: Profile, meta: { title: '档案' }, }]// 2. 创建Router对象, 并导出Routerconst router = new Router({ routes, mode: 'history', //默认使用的是hash, 这里替换为history方式没有# linkActiveClass: 'active', //linkActiveClass在路由上设置默认激活样式; active是App.vue中定义的style})router.beforeEach((to, from, next) => { document.title = to.matched[0].meta.title //获取meta.title next() //释放router,类似filter效果 console.log(to) // 当存在children时,直接取meta是没有值的。通过打印发现,在matched数组中有要的meta.title})// 3. 将router对象传入的Vue实例中export default router
//main.jsimport Vue from 'vue'import App from './App'import router from './router' //自动找router/index.jsVue.config.productionTip = false/* eslint-disable no-new */new Vue({ el: '#app', router, //将router传给Vue类中的$router, 所以router 与 $router是同一个对象 render: h => h(App)})
$router 和 $route 说明
- $router 和 $route 是Vue类的原型中定义的:源码中的install.js中通过
object.defineProperty(Vue.prototype, '$router', ...)
完成$router 和 $route的注册 - 所有自己创建的组件都继承自Vue类,所以都有$router 和 $route
- 将router (main.js中定义的)传给Vue类中的$router, 所以router 与 $router是同一个对象
- $route 表示当前被激活的route
- $router 与 $route 都来自Vue类中的this._routerRoot对象
- $router 和 $route 是Vue类的原型中定义的:源码中的install.js中通过
5.2 案例tabbar
学会封装组件
6 Vuex详解
6.1 Promise语法
// Promise基本使用new Promise((resolve, reject) => { setTimeout(() => { // 成功的时候调用resolve resolve("Hello World"); // 失败的时候调用reject // reject("error message"); }, 1000);}) .then((data) => { // data是resolve传过来的数据 // 1.100行的处理代码 console.log(data); console.log(data); console.log(data); console.log(data); console.log(data);}) .catch((err) => { // err是reject传过来的数据 console.log(err);});// Promise all 实现两个请求同时完成后再进行下一步操作Promise.all([ new Promise((resolve, reject) => { setTimeout(() => { resolve({ name: "why", age: 18 }); }, 2000); }), new Promise((resolve, reject) => { setTimeout(() => { resolve({ name: "kobe", age: 19 }); }, 1000); }),]).then((results) => { console.log(results);});
6.2 Vuex使用
Vuex就是为了提供这样一个在多个组件间共享状态的插件,Vuex扮演着大管家的角色(全局单例模式)
- vuex安装:npm install vuex --save
vuex使用步骤:1注册插件; 2创建对象并导出store; 3使用store
//index.jsimport Vue from 'vue'import Vuex from 'vuex'import {INCREMENT} from '@/store/mutations-types.js'const moduleA = { state: { // 模块中的state调用:this.$store.state.a.name name: 'zfcer' }, mutations: { // 模块中的mutations调用: this.$store.commit('mutations名字'), 调用方式不变 updateName(state, payload){ state.name = payload } }, actions: { aUpdateName(context) { setTimeout(() => { console.log('模块内actions执行了') // 模块内的actions只能调用模块内的mutations context.commit('updateName', '模块内的actions调用了模块内的mutations') }) } }, getters: { // 模块中的getters调用: this.$store.getters.fullName 调用方式不变。所以方法名不要与模块外的方法名重复 fullName(state){ return state.name + '111' }, fullName2(state, getters){ return getters.name + '222' }, fullName3(state, getters, rootState){ // rootState获取Root的state return getters.fullName2 + rootState.counter } }, modules: { }}// 1. 注册插件Vue.use(Vuex)// 2. 创建对象, 并导出export default new Vuex.Store({ state: { // 共享变量 counter: 1000, students: [ {id: 110, name: 'zfcer', age: 23}, {id: 111, name: 'best', age: 26}, {id: 112, name: 'one', age: 18}, ], info: { name: 'zfcer', age: 23, height: 1.87 } }, mutations: { // Vuex的store状态的更新唯一方式:提交Mutations // 方法中是 默认传入state参数的 // mutations中提交参数可以通过payload提交对象 [INCREMENT](state){ state.counter++ }, decrement(state){ state.counter-- }, incrementCountOne(state, payload){ console.log(payload) //payload是一个对象 }, incrementCount(state, payload){ console.log(payload.cnt1 + payload.cnt2) state.counter += payload.cnt1 }, addStudent(state, stu){ state.students.push(stu) }, updateInfo(state){ // 直接修改info中已有的属性是响应式的,这是因为Vue对属性做了监听可以做到响应式 state.info.name = 'zfcer best' // 直接在info中添加新属性不是响应式的 // state.info['address'] = '苏州' // Vue.set(state.info, 'address', '苏州') //利用Vue.set实现响应式 // 直接删除info中属性不是响应式的 // delete state.info.age // Vue.delete(state.info, 'age') //利用Vue.delete实现响应式 } }, actions: { // actions用来处理异步操作 // 方法一 // context上下文,可以理解为就是store对象 // payload传递参数 // aUpdateInfo(context, payload){ // console.log("---------------") // setTimeout(() => { // context.commit('updateInfo') // console.log(payload.message) // payload.success() //执行回调函数 // }, 1000) // } // 方法二 aUpdateInfo(context, payload){ return new Promise((resolve, reject) => { setTimeout(() => { context.commit('updateInfo') console.log(payload) resolve('内部执行成功') //传递信息可以被外部then获取 }, 1000) }) } }, getters: { // 与计算属性类似 powerCounter(state){ return state.counter * state.counter }, more20Stus(state){ return state.students.filter(s => s.age >= 20) }, more20StusLength(state, getters){ // 在getters中可以定义getters参数来获得getters中其他函数 return getters.more20Stus.length }, moreAgeStus(state){ // 通过内部创建函数来获得moreAgeStus传过来的参数 // return function(age){ // return state.students.filter(s => s.age >= age) // } return age => state.students.filter(s => s.age >= age) } }, modules: { // 模块最终会被加载到state中,还是一个实体 a: moduleA }})// 对象的解构const obj = { name: 'zfcer', age: 18, height: 1.87}const {name, height, age} = obj// 数组解构const names = ['zfcer', 'best', 'one']const [name1, name2, name3] = names
// main.jsimport Vue from 'vue'import App from './App'import router from './router'import store from './store'Vue.config.productionTip = false/* eslint-disable no-new */new Vue({ el: '#app', router, store, //3. 使用store render: h => h(App)})
vuex项目结构
7 axios
- 全局Axios
// 一、直接用axios是用的全局Axios// axios全局配置axios.defaults.baseURL = 'http://152.136.185.210:7878/api/m5'axios.defaults,headers.post['Content-Type'] = 'application/x-www-form-urlencoded'axios.defaults.timeout = 5000// 1. axios基本使用// 指定method的请求,默认是get请求axios({ // 局部配置 baseURL: 'http://152.136.185.210:7878/api/m5', url: '/home/multidata', method: 'get'}).then(res => console.log(res))// get请求axios.get('/home/multidata')// 带参数的get请求axios({ url: '/home/data', // 针对get请求的参数拼接 params: { type: 'pop', page: 1 }}).then(res => console.log(res))// 2. axios并发使用// then中获取结果集axios.all([axios({ url: '/home/multidata',}), axios({ url: '/home/data', // 针对get请求的参数拼接 params: { type: 'sell', page: 5 }})]).then(res => { //合并后的结果 console.log(res)})// then中获取分别的结果axios.all([axios({ url: 'http://123.207.32.32:8000/home/multidata',}), axios({ url: 'http://152.136.185.210:7878/api/m5/home/data', // 针对get请求的参数拼接 params: { type: 'sell', page: 5 }})]).then(axios.spread((res1, res2) => { //两个异步请求分别的结果 console.log(res1) console.log(res2)}))
- 局部Axios
// 二、axios实例创建与使用 const instance = axios.create({ baseURL: 'http://152.136.185.210:7878/api/m5', timeout: 5000, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }})instance({ url: '/home/data', method: 'get'}).then(res => { console.log(res)}).catch(err => { console.log(err)})
- 封装axios:创建network文件夹-->request.js工具类,在main.js中调用封装的request
//--------------------request.js---------------------------import axios from 'axios'export function request1(config, success, failure){ // 1.创建axios实例 const instance = axios.create({ baseURL: 'http://152.136.185.210:7878/api/m5', timeout: 5000 }) // 2.1.axios拦截器 instance.interceptors.request.use(config => { console.log(config) // (1)config中一些信息不符合服务器要求需要拦截 // (2)每次发送网络请求时,都希望在界面中显示一个请求的图标 // (3)某些网络请求(比如登录token),必须携带一些特殊信息 return config //释放拦截,如果不释放拦截就无法继续执行 }, err => { console.log(err) }) // 2.2.axios响应拦截 instance.interceptors.response.use(res => { console.log(res) // 做一些响应处理拦截 return res.data //只需要把data信息返回即可 }, err => { console.log(err) }) // 3.发送真正的网络请求 instance(config) .then(res => { // console.log(res); success(res) }) .catch(err => { // console.log(err) failure(err) })}export function request2(config){ // 创建axios实例 const instance = axios.create({ baseURL: 'http://152.136.185.210:7878/api/m5', timeout: 5000 }) // 发送真正的网络请求 instance(config.baseConfig) .then(res => { // console.log(res); config.success(res) }) .catch(err => { // console.log(err) config.failure(err) })}export function request3(config) { return new Promise((resolve, reject) => { // 创建axios实例 const isntance = axios.create({ baseURL: 'http://152.136.185.210:7878/api/m5', timeout: 5000 }) // 发送真正的网络请求 instance(config) .then(res => { resolve(res) }) .catch(err => { reject(err) }) })}export function request4(config) { // 创建axios实例 const instance = axios.create({ baseURL: 'http://152.136.185.210:7878/api/m5', timeout: 5000 }) // 发送真正的网络请求 return instance(config) //因为axios实例本身就是Promise}//-------------------main.js----------------------------// 三、axios封装import {request1, request2, request3, request4} from '@/network/request.js'request1({ url: '/home/multidata'}, res => { console.log(res)}, err => { console.log(err)})request2({ baseConfig: '/home/multidata', success: function(res){ console.log(res) }, failure: function(err){ console.log(err) }})request3({ url: '/home/multidata'}).then(res => console.log(res)).catch(err => console.log(err))request4({ url: '/home/multidata'}).then(res => console.log(res)).catch(err => console.log(err))
8 项目实战
将本地项目与远程仓库关联起来:
git remote add origin https://github.com/zfcer/testmall.gitgit push -u origin master
8.1 项目基本设置
- 项目结构
设置css样式和全局样式
- initialize.css:normalize.css,可以在github中查看normalize.css
- base.css:定义全局样式
vue.config.js 全局配置:起别名
module.exports = { configureWebpack: { resolve: { alias: { 'assets': '@/assets', 'common': '@/common', 'components': '@/components', 'network': '@/network', 'views': '@/views', } } }}
.editorconfig 全局配置项目编辑
root = true[*]charset = utf-8indent_style = spaceindent_size = 2end_of_line = lfinsert_final_newline = truetrim_trailing_whitespace = true
8.2 tabbar&导航栏组件
tabbar组件
- 导入写好的TabBar、TabBarItem、MainTabBar组件
- 安装vue-router,编写index.js路由,最后在mian.js中注入router
- 编写对应的views
- 在App.vue中引入组件,template中使用组件
导航栏组件
- 创建通用组件NarBar, 定义三个slot(left、center、right)
- 在views中使用NarBar组件
8.3 首页 - 轮播图&推荐组件
- axios封装
轮播图
- 创建swiper通用组件
- 在home文件夹下创建childComps/HomeSwiper(使用swiper通用组件)
- 在Home.vue通过axios获取数据,在HomeSwiper中通过props获取数据并展示
- 在Home.vue中使用HomeSwiper组件
推荐组件
- 在/src/views/home/childCops 创建 RecommendView.vue 组件
- 在Home.vue通过axios获取数据,在RecommendView中通过props获取数据并展示
- 在Home.vue中使用RecommendView组件