1.前言
Vue 是一套用于构建用户界面的渐进式框架。开发可以根据需求,逐渐递增所要的方式或者功能模块, vue的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
vue对项目的侵入性较大,使用者要按照框架所规定的某种特定规范进行开发,项目如果需要更换框架,则需要重新架构整个项目。
2.两大核心
---- css
---- html
{{message}}
{{title}}
- {{item.title}}
---- js
》Vue-CLI (Command Line Interface)
》Vue-CLI是Vue官方提供的脚手架工具,默认已经帮我们搭建好了一套利用Webpack管理vue的项目结构”
》命令安装: npm install @vue/cli -g
》检查版本: vue --version
》创建项目: vue create 项目名称
1.创建vue-project项目
vue create vue-project
2.运行项目
npm run serve (--port=8888) 默认是8080端口
3.打包项目
npm run build
webpack修改默认端口
在package.json中修改脚本配置:"serve": "vue-cli-service serve --port 8888"
创建项目流程的其它配置
(1.执行创建命令 vue create vue-project
(2.设置相关插件 一般选择default Vue 3
(3.然后进入项目 cd 项目名称 (上下键选择,空格确认勾选)
--》sass/scss --》 Airbnb --》 >(*) Lint on save dedicated config files
(*) Lint and fix on commit (requires Git)
该模板会被计算机保存在计算机所在的用户文件夹下(如:Icy-yun),如果删除vuerc文件,就会删除对应的模板
出现eslint的问题
1.webstorm配置
eslint报错,settings --》 搜索eslint --》 选择Eslint package: C:\wamp64\www\vue3\vuedemo\node_modules\eslint
--》 最后在App.vue右键选择Fix Eslint Problems
2.项目配置
eslint 报错, 然后在vue.config.js中添加
module.exports = {
....
lintOnSave:false
}
-- vue.config.js
var webpack = require('webpack');
// 注意这一行一定要加上,原本配置中没有,如果不加会导致下面报错webpack未定义
module.exports = {
//vue-cli3.0 里面的 vue.config.js做配置
// 开发服务器,跨域使用
// devServer: {
// proxy: {
// '/api': {
// target: 'http://47.96.133.174:9090', // 后台接口域名
// ws: true, //如果要代理 websockets,配置这个参数
// secure: false, // 如果是https接口,需要配置这个参数
// changeOrigin: true, //是否跨域
// pathRewrite:{
// '^/api': '/'
// }
// }
// }
// },
configureWebpack:{
resolve:{
alias:{
'assets':'@/assets',
'components':'@/components',
'network':'@/network',
'utils':'@/utils',
'views':'@/views',
}
},
plugins:[
new webpack.ProvidePlugin({
jQuery: 'jquery',
$: 'jquery'
})
]
},
// 注意:不要添加下面一行,否则路由引入会出现异常
// publicPath:'./'
};
1.在assets同级目录下的文件,可以直接引入
(1.main.js中 import 'assets/font/iconfont.css'
(2.App.vue
background:url(assets/images/bg-white.png)
@import("assets/font/iconfont.css")
2.在组件内引入图片, html,和css同理
3.组件内引入其它组件
import HelloWorld from '@/components/HelloWorld.vue'
1.基本语法
{[msg}}
数据只第一次时显示,不响应式
{[msg}}
, 内容原封不动的展示, 包含空格,换行
, 就相当于插值表达式的功能
, 可以解析标签, 如果有scoped,则不会受到其内部css的影响
data:{
msg:'test message',
title:'Title
`
}
2.使用实例
---- src/App.vue文件内容
{{msg+''+str}}
{{num+1*2}}
{{num}} aa bb cc
{{num}}
{{url}}
---- src/App.vue文件内容
1.基本语法
》计算属性关键词: computed。
》计算属性在处理一些复杂逻辑时是很有用的。
computed:{
site: {
//getter
get: function () {
return this.name +"" + this.url
},
// setter
set: function (newValue) {
var names = newValue.split(" ")
this.name = names[0]
this.url = names[names.length - 1]
}
}}
2.使用实例
---- src/App.vue文件内容
{{name}}-{{slogen}}
{{name + '-' + slogen}}
{{getTitle()}}
{{title}}
总价:¥{{books[0].price+books[1].price+books[2].price+books[3].price}}
总价:¥{{totalPrice}}
1.基本语法
》在前端开发中,需要经常和用户交互
》绑定事件监听器指令: v-on
》缩写:@(语法糖)
》参数: $event
》v-on事件修饰符号
.stop阻止事件冒泡
.self 当事件在该元素本身触发时才触发事件
.capture 添加事件侦听器是,使用事件捕获模式
.prevent 阻止默认事件
.once事件只触发一次
2.使用实例
---- src/App.vue文件内容
1.基本语法
》 v-if,和v-show
v-if是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-show就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS进行切换
》v-if v-else
》v-if v-else-if v-else
2.使用实例
---- src/App.vue文件内容
{{msg}}
{{msg}}
111111111
222222222
111111111
222222222
333333333
1.基本语法
》遍历指令: v-for
》遍历数组v-for="(item, [index]) in数组”
》遍历对象v-for=" (value,[key], [index]) in对象”
》vue中列表循环需加:key="唯一标识"唯一标识可以是item里面id index等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件
》key的作用主要是为了高效的更新虚拟DOM,使用diff算法的处理方法,对操作前后的dom树同一层的节点进行对比,一层一层对比
2.使用实例
---- src/App.vue文件内容
- {{item}}
- {{index+1}}--{{item}}
- {{index+1}}--{{item}}
- {{index+1}}--{{item.name}}--{{item.price}}
- {{index+1}}--{{item.name}}--{{item.price}}
1.基本语法
》v-model指令的本质是:它负责监听用户的输入事件,从而更新数据,并对一些极端场景进行一些特殊处理。同时,v-model会忽略所有表单元素的value、checked、selected特性的初始值,它总是将vue实例中的数据作为数据来源。然后当输入事件发生时,实时更新vue实例中的数据。
》实现原理:
》v-model的修饰符号:
.lazy 懒加载修饰符
.number修饰符让其转换为number类型
.trim修饰符可以自动过滤掉输入框的首尾空格
.keyup.enter 修饰符
2.使用实例
---- src/App.vue文件内容
{{typeof(msg1)}}
{{sex}}
{{tags}}
{{language}}
》组件化是Vue的精髓,Vue开发就是由一个一个的组件构成的组件的分类:
》页面级组件
业务上可复用的基础组件
与业务无关的独立功能组件
》组件开发三要素( prop,自定义事件,slot)
prop用于定义组件的属性。
自定义事件用于触发组件的事件。
slot用于组件功能的扩展。
》组件设计需要考虑的问题
可扩展性强
组件中方法函数的抽离,便于复用,适用程度高。
文档清楚详细
颗粒度合适,适度抽象
功能尽可能单一,代码行数适中
1.基本的生命周期
Vue3中组件的生命周期函数, (与data,methods同级)
beforeCreate() {
console.log("实例刚刚被创建");
},
created() {
console.log("实例已经创建完成");
},
beforeMount() {
console.log("模板编译之前”);
}
mounted(){
console.log("模板编译完成");
},
beforeUpdate(){
console.log("数据更新之前");
},
updated() {
console.log("数据更新完成");
},
beforeUnmount(){
console.log("实例销毁之前");
},
unmounted(){
console.log(“实例销毁完成");
},
2.特殊生命周期
// 特殊的生命周期
activated(){
console.log("keep-alive缓存的组件激活时调用);
使用 标签包含所要监听的内容
},
deactivated({
console.log('keep-alive缓存的组件停用时调用');
},
3.常用场景
activated(){
console.log("keep-alive缓存的组件激活时调用);
// 使用 标签包含所要监听的内容
this.$nextTick(()=>{
//等待下一次DOM更新,具有延迟的效果。
// 调用子组件的方法
this.$refs.username.abc()
})
},
1.基本语法
》Vue 实现了一套内容分发的API,这套API的设计灵感源自Web Components规范草案,将元素作为承载分发内容的出口。
》插槽可以实现组件的扩展性,抽取共性,保留不同
slot 插槽占位
template 向子组件的插槽块添加内容
2.使用实例
---- MyBar.vue文件内容(子组件)
提交
{{subdata.user.name}}
提交
---- MyMain.vue文件内容(父组件)
1.对象方法
this.$nextTick(()=>{ })
this.$nextTick()将回调延迟到下次DOM更新循环之后执行。在修改数据之后立即使用它,然后等待DOM更新,一般放入activated
2.组合式api
import { createApp, nextTick } from 'vue'
// async 异步方法
const changeMessage = async newMessage => {
message.value = newMessage
await nextTick()
console.log('Now DOM is updated')
}
// promise 异步方法
nextTick().then(res=>{
....
})
$forceUpdate
用法:迫使组件实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
watch:{
// 监听路由的变化
$route(to,from){
console.log("已经变化了")
}
}
子组件是被组合到父组件里面,因此父组件是会共享父组件定义的样式,因此只有使用scoped 隔绝样式污染。
...
1.基本语法
// 数组格式接收数据
//props:['msg','title','article'],
// json格式接收数据,可以不回default缺省值,required:true,需求传入参数
props:{msg:{type:String,default:"#####"},title:{type:String,required:true},article:{type:Array}},
注意点:
type类型,都是大写的类型 String, Array, Objejct, Boolean
2.使用实例
---- App.vue文件内容(父组件)
---- MyMain.vue文件内容(子组件)
{{msg}}
{{show()}}
{{title}}
{{article}}
{{item}}
1.基本语法
子组件向父组件派发一个自定义事件, 并向这个事件传入实参
父组件的自定义事件被触发,则会收到传递的实参。
2.使用实例
---- MyConn.vue文件内容(子组件)
触发事件
methods:{
changenum(num){
this.$emit('mycountevent',num);
}
}
---- MyMain.vue文件内容(父组件)
methods:{
mydemo(data){
this.count += data;
}
}
子组件获取父组件的属性与方法
---- App.vue文件内容(根组件)
appmet(){
console.log('我是APP Root组件中的appmet方法')
}
---- MyMain.vue文件内容(父组件)
changen(){
this.count++;
console.log('这是组件MyMain中的changen方法');
},
---- MyConn.vue文件内容(子组件)
触发事件
one(){
console.log('这是子组件MyConn中的one方法');
this.$parent.changen(); 获取父组件的属性与方法
this.$parent.$parent.appmet(); 获取级祖父组件的属性与方法
this.$root.appmet(); 获取根组件的属性与方法
},
父组件获取子组件的属性与方法
1.基本语法
取别名,确定子组件的属性
this.$refs.aaa.changeone(); 获取子组件的方法
let num = this.$refs.aaa.num; 获取子组件的属性
2.使用实例
---- MyConn.vue文件内容(子组件)
子组件触发事件
changenum(num){
this.$emit('mycountevent',num); 向父组件传递事件
},
---- MyMain.vue文件内容(父组件)
触发事件
取别名,确定子组件的属性
mydemo(data){ 接收数据
this.count += data;
},
two(){
this.$refs.aaa.changeone(); 获取子组件的属性与方法
let num = this.$refs.aaa.num;
}
安装模块
》在该项目下安装axios npm install axios -S
》打开wampserver服务
axios.get('http://localhost/axiosdemo/getapi.php?id=1&name=小明') .then(res=>{
console.log(res);
this.links = res.data;
console.log(typeof(res.data))
}).catch(err=>{
console.log(err);
})
axios.post('url',data,{
transformRequest:[ // 将数组格式转化成字符串
function(data) {
let ret = "";
for (let it in data) {
ret+= encodeURIComponent(it) +"="+ encodeURIComponent(data[it])+"&";
}
return ret;
}
],
headers: {
"Montent-Type" : "application/x-www-form-urlencoded"
}
}).then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
});
import axios from 'axios';
// 方法一:封装成函数
function request(config){
//使用axios创建一个实例对象
const instance = axios.create({
baseURL: 'http://api.eduwork.cn/admin', // 基本的url
timeout: 5000, // 超时限制
});
//请求拦截器
instance.interceptors.request.use( config=>{
//config.headers.token = "123456" // 可以在修改数据后,再返回
return config;
}, err =>{
return Promise.reject(err); // 返回出错信息
});
//响应拦截器
instance.interceptors.response.use( response =>{
console.log(response);
return response;
}, err =>{
return Promise.reject(err); // 返回出错信息
});
return instance(config);
}
// 方法二:封装成方法
function http(){
//使用axios创建一个实例对象
const instance = axios.create({
baseURL: 'http://api.eduwork.cn/admin', // 基本的url
timeout: 5000, // 超时限制
});
instance.interceptors.request.use( config=>{
//config.headers.token = "123456" // 可以在修改数据后,再返回
return config;
}, err =>{
return Promise.reject(err); // 返回出错信息
});
return {
//get请求
get(url,params){ // params是一个json对象
return instance.get(url,{
params
})
},
//post请求
post(url,data){
return instance.post(url,data,{ // data是一个数组,包含多个json对象
transformRequest:[ // transformRequest是将一个数组,改变成字符串
function(data){
let str = "";
for(let key in data){
str += encodeURIComponent(key)+"=' +encodeURIComponent(data[key])+ '&';
}
console.log(str);
return str;
}
],
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
})
},
//delete请求
delete(url){
return instance.delete(url)
}
}
}
1.基本知识
延迟加载动态导入和路由模式解析
》路由是由多个URL组成的,使用不同的URL可以相应的导航到不同的位置
》Vue-Router在切换页面时是没有重新进行请求的,使用起来就好像页面是有状态的一样,借助了浏览器的History APl来实现的,这样可以使得页面跳转而不刷新,页面的状态就被维持在浏览器中
》vue-router中默认使用的是hash模式,也就是会出现URL中带有#号
》有三种模式
.Hash:使用URL的hash值来作为路由,用来指导浏览器动作的,对服务器端完全无用,支持所有浏览器
.History: 以来HTML5 History API和服务器配置。
.Abstract:支持所有javascript运行模式。如果发现没有浏览器的API,路由会自动强制进入这个模式。
2.使用实例
----./src/router/index.js文件内容(创建路由)
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/', // 路径
name: 'Home', // 名称
component: Home // 组件 引入方式一
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue') // 引入方式二
}
];
const router = createRouter({ // 创建路由
//history: createWebHistory(process.env.BASE_URL), // 历史记录
history: createWebHashHistory(process.env.BASE_URL), // 历史记录,url会有#号出现, 默认
routes //路由规则
});
export default router //暴露路由
----./src/main.js文件内容(配置路由)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router' // 添加路由文件
createApp(App).use(router).mount('#app') // 配置路由
----./src/App.vue文件内容(使用路由)
// 加载的数据放置的位置
const home = ()=>import("../xxxx")
path:"/:pathMatch(.*)" // vue3
----./src/App.vue文件内容(使用路由)
1.基本语法
单独的路由规则 ,不能直接设置为多级路由。
2.使用实例
let MyOrder = () => import('../views/MyOrder.vue');
let MySetting = () => import('../views/MySetting.vue');
{
path: '/about',
name: 'About', // name只用于router-view的name属性
component:About,
children: [ //创建子路由
{
path:'',
component: MySetting //默认显示的子路由
},
{
path: 'order', // about/order
component: MyOrder
},
{
path: 'setting', // about/setting
component: MySetting
},
]
}
1.基本语法
》传递参数主要有两种类型: params和query”params的类型:
》params的类型:
配置路由格式: /user/:id (动态路由)
传递的方式:在path后面对应的值:to=” '/user /' +uid"
传递后形成的路径: /usei/9,/user/zs
接收参数:$route.params.id
》query的类型:
配置路由格式: /user,正常配置
传递的方式: 对象中使用query的key作为传递方式:to= {path:' /' , query:lid:1,name:'abc'l}
传递后形成的路径:/user?id=9,/user?id=zs
接收参数: $route.query.name
2.使用实例
---- src/views/About.vue文件内容
{{items.title}}
-
文章一
-
文章二
---- src/router/index.js文件内容
{
path: '/about',
name: 'About', // name只用于router-view的name属性
component: About,
children: [ //创建子路由
{
path:'',
component: MySetting //默认显示的子路由
},
{
path: 'order', // about/order
component: MyOrder
},
{
path: 'setting', // about/setting
component: MySetting
},
{ // about/page/11
path:'page/:id', // 一定要添加:号
component: MyPage
},
{ // about/article?name=hello&age=100
path:'article',
component: MyArticle
}
]
}
---- src/views/MyPage.vue文件内容
这是文章的模板
文章ID:{{$route.params.id}}
---- src/views/MyArticle.vue文件内容
这是文章的页面:
name:{{$route.query.name}}
age:{{$route.query.age}}
》重定项:
重定向也在routes配置中完成,要从重定向/a到b
const routes = [{ path: '/home', redirect: '/' }]
const routes = [ path: "/home' , redirect: { name: 'homepage' } }]
path: '/search/:searchText',
redirect: to =>{
return { path: '/search', query: { q: to.params.searchText } }
),
》别名:
别名/as/home表示用户访问时/home,URL保持不变/home,但将被匹配,就像用户正在访问时一样/。
const routes = [{ path: '/' , component: Homepage, alias: "/home"}]
alias: ['people', 'list']
alias: ['/:id' , '']
---- src/router/index.js文件内容
const routes = [
{
path: '/', // 路径
name: 'Home', // 名称
component: Home // 组件
/*components:{ // 多个路由组成的组件
default:Home, // 默认页面
About
}*/
},
{
path: '/home', // 路径
name: 'RedirectHome', // 名称
redirect:{name:'Home'}, // 重定向
alias:['a','b','c'], // 取别名
component: Home // 组件
},
{
path: '/about',
name: 'About', // name只用于router-view的name属性
component: About,
children: [ //创建子路由
{
path:'',
component: MySetting //默认显示的子路由
},
{
path: 'order', // about/order
component: MyOrder
},
{
path: 'setting', // about/setting
component: MySetting
},
{ // about/page/11
path:'page/:id', // 一定要添加:号
// ** path子路由重定向query子路由(无法反过来)
// $route = to
redirect :to => {
return {path: 'article', query: {name: 'zhangsan', age: to.params.id}};
},
// ** 取别名
alias:['p/:id','x/:id'],
component: MyPage
},
{ // about/article?name=hello&age=100
path:'article',
component: MyArticle
}
]
}
];
1.基本语法
》导航守卫主要用来通过跳转或取消的方式守卫导航。
》前置守卫
// to: Route:即将要进入的目标路由对象,
// from: Route:当前导航正要离开的路由
// next: 将要跳转的页面
router.beforeEach((to, from,next)=>{
return false
})
》后置钩子
router.afterEach((to, from) =>{
return false
})
》有多种机会植入路由导航过程中:全局的,单个路由独享的,或者组件级的。
1.全局导航守卫
2.路由独享的守卫
3.组件内的守卫(设置标题或者失败跳转)
2.使用实例
// 1.路由独享守卫
---- src/router/index.js文件内容
{
path: '/', // 路径
name: 'Home', // 名称
component: Home, // 组件
meta: {
title: '222222' // 元数据,可以通过全局的前置守卫,设置标题
},
beforeEnter: (to, from) => { // 路由独享的守卫(局部)
return false;
},
/*components:{ // 多个路由组成的组件
default:Home, // 默认页面
About
}*/
},
// 2.全局守卫
// 前置守卫(全局)
router.beforeEach((to, from) => { // 与const router = createRouter 同级
// return false // next已经被取消, false 表示禁用跳转,true表示放行
console.log(to.fullPath);
console.log(from.fullPath);
document.title = to.meta.title; // 通过元数据,来设置标题
});
// 后置勾子(全局)
router.afterEach((to, from) => { // 与const router = createRouter 同级
// return false // next已经被取消, false 表示禁用跳转,true表示放行
console.log('----' + to.fullPath);
console.log('----' + from.fullPath);
});
// 3.组件内部路由守卫(路由函数)
---- src/views/Home.vue文件内容
beforeRouteEnter(to,from,next){
document.title = "主页"; // 设置标题
next(vm => { }) // 组件内的守卫,与components同级
}
beforeRouteLeave(to,from,next){
next(vm => { })
}
1.基本语法
》keep-alive是Vue内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
include:正则表达式,exclude: 正则表达式
》router-view是vue-router内置组件,如果直接包含在keep-alive里面,所有路径匹配到的组件都会被缓存。
// 1.完整的缓存标签
// 2.选择性显示
// 目标路由定义: meta: {keepAlive: true} // 这个是需要keepalive的
2.使用实例
activated(){
console.log('keep-alive缓存的组件激活时调用 ');
this.$router.push(this.path);
},
deactivated(){
console.log('keep-alive缓存的组件停用时调用 ');
},
可以直接在页面上输出。
{{$route}} // $route表示当前路径的路由
{{$router}} // $route表示当前路径的路由
// 1.对象式api
this.$router.currentRoute 当前路由对象
this.$router.currentRoute._value 当前路由参数
{
fullPath: "/tablist/detail?scene_id=110"
hash: ""
href: "/tablist/detail?scene_id=110"
meta:{
title: "景区详情"
}
name: "detail"
params: {}
path: "/tablist/detail"
}
// 2.组合式api,
route = useRoute()
{
fullPath: "/tablist/detail?scene_id=110"
hash: ""
href: "/tablist/detail?scene_id=110"
meta:{
title: "景区详情"
}
name: "detail"
params: {}
path: "/tablist/detail"
}
Action -> State -> View -> Action ->… 循环
》Vuex是一个专为Vue.js应用程序开发的状态管理模式。
.就是一个加强版的data!在单页应用中会有一个data函数,管理当前应用的状态。处理大量的需要在组件间传递的数据,直接定义一个全局的data属性保存就行了。
.如果我们的页面比较简单,切记千万不要没事找事引入Vuex,我们使用Vuex是因为项目变得复杂之后,有很多数据需要在父组件、子组件和孙组件之间传递,
处理起来很繁琐,于是就需要Vuex这样一个可以对这一部分数据进行统一管理的东西。
.也是响应式
》什么情况需要使用Vuex管理状态在多个组件间共享?
大型项目中组件很多,多个组件中共用的数据
例如:用户的登录状态、用户名称、头像、地理位置信息等
例如:商品的收藏、购物车中的物品。
(注意router使用的$route)
1.基本结构
import { createStore } from 'vuex'
export default createStore({
state: {
num:0
},
mutations: {
},
actions: {
},
modules: {
}
})
2.使用实例
---- src/Contents/HelloWorld.vue文件内容
在子组件helloworld中应用Home中的Data
{{count}}
在HelloWorld组中使用全局的状态
{{$store.state.num}}
---- src/views/Home.vue文件内容
这是在单页模板中应用
使用全局的状态
{{$store.state.num}}
1.Mutations同步操作
---- src/store/index.js文件内容
import {createStore} from 'vuex'
export default createStore({
state: {
num: 0,
dnum: 0
},
mutations: { // 可以通过devtools工具查看状态管理
sub(state) {
state.dnum--;
},
add(state) {
state.dnum++;
},
sub2(state,count) { // 传入一个参数
state.dnum-= count;
},
add2(state,count) {
state.dnum+= count;
},
sub3(state,payload) { // 传入多个参数(普通方式)
state.dnum-= (payload.count+payload.num);
},
add3(state,payload) {
state.dnum+= (payload.count+payload.num);
},
sub4(state,p) { // 传入多个参数(对象方式)
state.dnum-= (p.payload.count+p.payload.num);
},
add4(state,p) {
state.dnum+= (p.payload.count+p.payload.num);
},
},
actions: {},
modules: {}
})
**2.组件调用 **
---- src/views/Home.vue文件内容
使用Mutations来修改状态
DNUM:{{$store.state.dnum}}
使用Mutation来修改状态,带一个参数
使用Mutation来修改状态,带多个参数
methods:{
add1(){
this.$store.commit('add');
},
sub1(){
this.$store.commit('sub');
},
add2(){ // 使用带一个参数
let count = 2;
this.$store.commit('add2',count);
},
sub2(){
let count = 2;
this.$store.commit('sub2',count);
},
add3(){ // 使用带多个参数
let count = 2;
let num=1;
const payload = { // 必需使用对像的方式传入多个参数
count,num
};
this.$store.commit('add3',payload); // 普通方式
},
sub3(){
let count = 2;
let num=1;
const payload = {
count,num
};
this.$store.commit('sub3',payload);
},
add4(){ // 使用带多个参数
let count = 2;
let num=1;
const payload = { // 必需使用对像的方式传入多个参数
count,num
};
this.$store.commit({ // 使用对象的方式传递
type:'add4',
payload
});
},
sub4(){
let count = 2;
let num=1;
const payload = {
count,num
};
this.$store.commit({
type:'sub4',
payload
});
},
}
---- src/views/Home.vue文件内容
使用getter中的计算属性:{{$store.getters.vxnum}}
购物车的总价:{{$store.getters.totalprice}}
贵重物品的总价:{{$store.getters.specialtotalprice}}
贵重物品(self)的总价:{{$store.getters.specialtotalpriceself(10)}}
---- src/store/index.js文件内容
import {createStore} from 'vuex'
export default createStore({
state: {
num: 0,
dnum: 0,
cartlist: [
{name:'《细说PHP》',price:10},
{name:'《细说PHP》',price:20},
{name:'《细说PHP》',price:30},
{name:'《细说PHP》',price:40},
]
},
getters:{ // 与mutations,state同级
// 第一个参数就是state
vxnum(state){
return Math.pow(state.num,2);
},
totalprice(state){
return state.cartlist.reduce((s,n) => s+n.price,0);
},
goodsgt(state){ // 这里返回的是数组中的元素
return state.cartlist.filter(n => n.price>20);
},
specialtotalprice(state,getters){ // 以getters中所有的变量作为参数
return getters.goodsgt.reduce((s,n)=>s+n.price,0)
},
goodsself(state){ // 返回一个方法,作为回调函数
return (price)=>state.cartlist.filter(n => n.price>price);
},
specialtotalpriceself(state,getters){ // 因此,可以通过返回一个回调函数,向getters中传递参数
return function(price){
return getters.goodsself(price).reduce((s,n)=>s+n.price,0)
}
}
},
mutations: { // 可以通过devtools工具查看状态管理
...
},
actions: {},
modules: {}
})
派发异步操作
---- src/views/Home.vue文件内容
使用action改变状态
methods:{
cnum( params=null){
if (params ===null)
this.$store.dispatch('demo'); // 分发数据
else
this.$store.dispatch('demo2',params); // 分发数据
},
}
---- src/store/index.js文件内容
import {createStore} from 'vuex'
export default createStore({
state: {},
getters:{},
mutations: {
cnum(state,params=null){
if(params==null)
state.num = 99;
else
state.num = params;
}
},
actions: { // 与state,getters,mutations同级
demo(context){
setTimeout(()=>{
// context.state.num = 99;
context.commit('cnum'); // 通过mutations改变状态
},300)
},
demo2({state,commit,getters},payload){ // 这个效果同上,其实是context只是一个对象,包含所列的所有属性
setTimeout(()=>{
state.num = payload;
commit('cnum',payload); // payload也可以是一个对象
},300)
},
actionA({commit}){
return new Promise((resolve, reject) =>{
setTimeout(()=>{
commit('someMutation');
resolve()
},1000)
})
},
actionB({dispatch,commit}){
return dispatch('actionA').then(()=>{
commit('someOtherMutation')
})
}
},
modules: {}
})
---- src/store/index.js文件内容
import {createStore} from 'vuex'
// 模块划分
const user={
state:{
name:"xiaoming",
id:'0101'
},
getters:{
// 返回当前对象的数据
fullname(state){
return state.name + state.id;
},
// 调用当前对象的getters
fullname2(state,getters){
return getters.fullname + '22222';
},
// 调用全局的state,调用全局的getters没有必要,不过在actions中可以通过rootGtters获取全局的getters
fullname3(state,getters,rootState){ // rootState表示根对象的状态
return getters.fullname + rootState.num;
}
},
mutations:{
setname(state,payload){
state.name = payload;
}
},
actions:{
dosome({state,commit}){ // context中包含所有可用的属性,包含父类的rootState,rootGetters
setTimeout(()=>{
commit('setname','hello vuex');
},2000)
}
},
};
const article={};
const cart={};
const goods={};
// 文件划分,
// 》划分state 可以设置常量
// 》划分getters,mutations,actions,子模块
// 1.把getters中的对象(因为省略了键名)保存在js文件中,使用export default{} 暴露
// 2.使用import getters from './getters' 引入文件
export default createStore({
state: { },
getters:{ },
mutations: { },
actions: { },
modules: {
user,
article,
cart,
goods
}
})
》使用传统的option配置方法写组件的时候问题,随着业务复杂度越来越高,代码量会不断的加大;
由于相关业务的代码需要遵循option的配置写到特定的区域,导致后续维护非常的复杂,
同时代码可复用性不高,而composition-api就是为了解决这个问题而生的。
》Composition API字面意思是组合API,它是为了实现基于函数的逻辑复用机制而产生的。主要思想是,我们将它们定义为从新的setup函数返回的JavaScript变量,而不是将组件的功能(例如state.method、computed等)定义为对象属性。
》setup函数在创建组件之前被调用,所以在setup被执行时,组件实例并没有被创建。因此在setup函数中,我们将没有办法获取到this。
》接收context是setup()的第二个参数是一个上下文对象,这个上下文对象大致包含了这些属性,
注意:在setup()函数中无法访问this
const MyComponent = {
setup(props, context) {
context.attrs
context.slots
context.parent
context.root
context.emit
context.refs // 没有refs
}
}
---- src/views/About.vue文件内容(组合api)
count:{{data.count}}
double:{{data.double}}
1.基本语法
》setup()函数是vue3中专门新增的方法,可以理解为Composition Api的入口.
》执行时机在beforecreate之后,created之前执行. 调用时机, this指向,函数参数,返回值
》接收props数据, ref 进行绑定标签
2.使用实例
// 1.组合式api中的ref
我是hme元素
// 2.组合式api中的ref
1.基本语法
》ref()函数用来给定的值创建一个响应式的数据对象,ref()的返回值是一个对象,这个对象上只包含一个.value属性.
》reactive是用来创建一个响应式对象
》将ref响应式数据挂载到reactive中,当把ref()创建出来值直接挂载到reactive()中时,会自动把响应式数据对象的展开为原始的值,不需要通过.value就可以直接访问到
》双向绑定
》toRefs()解构响应式对象
》readonly将响应式数据变回原使数据
// reactive的对象 ,需要使用toRefs拆开暴露 , 并且初始化时需要定义直接子元素,不然即使对象 被赋值 也无法获取到新的数据,
// 如 1.settings = reactive({}), 并且使用 params = {xxxx},则settings的 数据会一直是空
// 2.赋值 params.name = "3333",params[xxx] = 'xxx'则有效, 无论初始值是否为空。
// toRefs是对reactive的解析作用。 而对象属性的变化不会触发页面的更新 。
// v-model不能绑定reactive创建的对象的子元素,而使用data返回的参数就可以。(element-ui的表单限制)
// 赋值reactive对象(直接赋值对象,无效)
const setReactive = (obj,newobj)=> {
Object.keys(newobj).forEach(item=>{
obj[item] = newobj[item]
})
}
2.使用实例
---- src/views/ComApi.vue文件内容
常用api应用
原生的:{{num}}
ref的变量:{{num2}}
reactive中的对象2:{{name}}、{{age}}
isRef的变量:{{num3}}
num2
user.name
user.age
1.基本知识
》setup和Vue的Composition API的引入,开辟了新的可能性,但为了能够充分利用Vue Router的潜力,我们将需要使用一些新功能来替换对访问this和组件内导航保护的使用。
》由于我们无权访问setup的内部this,因此无法直接访问this.$router或this.$route了,相反,我们使用useRouter和useRoute函数。
》请注意,我们仍然可以访问$router和$route在模板中,因此无需在router或之route内返回setup.
》尽管您仍可以将组件内导航保护与setup功能结合使用,但Vue Router会将更新和离开提供Composition&API函数:
1.onBeforeRouteLeave((to, from) =>{})
2.onBeforeRouteUpdate(async (to, from)=>{})
2.使用实例
》computed()用来创建计算属性,返回值是一个ref的实例。
---- src/views/ComputedDemo.vue文件内容
计算属性
firstname:
lastname:
{{firstname+'、'+lastname}}
{{fullname}}
age:
{{age}}岁,{{show}}
》watch()函数用来监视某些数据项的变化,从而触发某些特定的操作
1.基本语法
watch(()=>{
console.log(count.value, 'value');
})
const state = reactive({
count: 0
})
watch(()=>state.count,(count, prevCount)=>{
console.log(count, prevCount);//变化后的值变化前的值
})
watch([()=> state.count, ()=> state.msg].([count, msg], [prevCount,prevMsg])=>{
console.log(count, msg);
console.log('------------');
console.log(prevCount, prevMsg);
})
》第三个参数immediate:其值是true或false:确认是否以当前的初始值执行handler的函数。
》watchEffect立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。
2.使用实例
---- src/views/WatchDemo.vue文件内容
侦听器watch()api
1.基本知识
生命周期与Composition之间的映射关系
》 beforeCreate -> use setup()
》 created -> use setup()
》 beforeMount -> onBeforeMount
》 mounted -> onMounted
》 beforeUpdate -> onBeforeUpdate
》 updated -> onUpdated
》 beforeUnmount -> onBeforeUnmount
》 unmounted -> onUnmounted
在新版的生命周期函数,可以按需导入到组件中,且只能在setup()函数中使用.
2.使用实例
---- src/views/LifeHook.vue文件内容
生命周期api
1.基本语法
》父子组件:通过props,$emit,【$root,$parent,$children】
》非父子组件: Vuex实现,父子层层传递、$ref
》Vue官网建议,在正常情况下,这两种方式已经能满足绝大多数甚至所有的业务需求,对于应用程序代码应优先使用它们处理。
》provide/inject这对选项允许一个祖先组件向其所有子孙后代组件注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
》provide就相当于加强版父组件prop,可以跨越中间组件,inject就相当于加强版子组件的props
// 1.对象式访问
根提供变最
provide(){
return {
title:this.title,
}
},
后代注入变理
inject: [ "message" ],
// 2.组合式访问
setup(){
const name = ref("Imonkey')
const obj = reactive({
name: "lmonkey",age: "3"
})
provide("name" , name);
provide("network" , obj)
}
setup(){
//用法: inject(key)
const name = inject('name')
const animal = inject('animal')
return {name,animal}
}
2.使用实例
---- src/views/RootApp.vue文件内容
---- src/components/ThreeComp.vue文件内容(孙子组件)
前言:
Axios简单的理解就是ajax的封装
Axios是一个基于promise的 HTTP库,使用Promise管理异步,告别传统callback方式
支持node端和浏览器端
丰富的配置项,支持拦截器等高级配置
项目的两种编程方式-模板式编程和接口式编程
RestFul API规范(URL,HTP,版本,状态码,返回值,请求条件等规范)
. GET (SELECT) :从服务器取出资源(一项或多项)。
· POST (CREATE):在服务器新建一个资源。
· PUT (UPDATE):在服务器更新资源(客户端提供改变后的完整资源)
. PATCH (UPDATE):在服务器更新资源(客户端提供改变的属性)
. DELETE (DELETE) :从服务器删除资源。
我们配置接口时,这只是一种规范,只是建议大家按照规范来使用而已。
(1.首先引入模块(cdn服务)
(2.请求数据
// 默认get方法
axios('http://localhost/axios/getapi.php').then(res=>{
console.log(res);
console.log(res.data)
})
// options参数传入
axios({
method:"post", // 默认get方法
url:'http://localhost/axios/getapi.php',
headers:{ // 使用post方法必需添加这个请求头
'content-type':'application/x-www-form-urlencoded'
},
data:{ // get方法是params参数,post方法是data参数
usename:"zhangsan", // 但这种请求数据的方式的参数都是json对象格式
age:10,
sex:'nan'
}
}).then(res=>{ // 这里的服务器类型是http
console.log(res);
console.log(res.data)
})
安装axios模块
npm install axios -S
1.常用的方法
》import axios from 'axios';
》axios.request(config)
》axios.get(url[, config])
》axios.delete(url[, config])
》axios.head(url[, config])
》axios.post(url[, data[, configll)
》axios.put(url[, data[, config]])
》axios.patch(url[, data[, config]])、
// config参数其实就是一个json对象, 包含url,method,headers,data,params等
**2.使用实例 **
import axios from "axios"; // 引入axios模块
// 类型一: Get类型
// get请求,没有参数
axios.get('http://localhost/axiosdemo/getapi.php?username=abc').then(res=>{
console.log(res);
});
// get请求2,有参数
axios.get('http://localhost/axiosdemo/getapi.php', {
params:{id:1}
}).then(res=>{
console.log(res);
});
// 类型二:Post类型
// post请求,有参数,(data 是使用query字符串类型的参数)
axios.post('http://localhost/axiosdemo/getapi.php',
'name=测试&url=eduwork.cn'
).then(res=>{
console.log(res);
});
// post请求2,有参数,(data 是使用数组类型的参数)
const options = {
transformRequest:[ // 将数组格式转化成字符串
function(data) {
let ret = "";
for (let it in data) {
ret+= encodeURIComponent(it) +"="+ encodeURIComponent(data[it])+"&";
}
return ret;
}
],
headers: {
"Montent-Type" : "application/x-www-form-urlencoded"
}
}
axios.post('http://localhost/axiosdemo/getapi.php',
[name:'测试',url:'eduwork.cn'],
options
).then(res=>{
console.log(res);
});
1.基本语法
》ajax请求过多对页面性能可能会有影响,以及代码不美观,代码过于臃肿,所以我们可以使用axios的并发请求axios.all()
》axios.all()和promise.all()方法是一个道理
》axios.all()这个方法在axios的构造函数是没有的,没在实例对象上。
2.使用实例
// all方法, 并发地发送多个请求,返回一个数组
axios.all([
axios.get('http://localhost/axiosdemo/getapi.php?id=1&name=小明'),
axios.get('http://localhost/axiosdemo/getapi.php?id=2&name=小红')
]).then(res => {
console.log(res) // 返回的是一个数组
}).catch(err => {
console.log('错误是'+err)
});
// .spread方法 将返回的数组再分开处理(不建议)
axios.all([
axios.get('http://localhost/axiosdemo/getapi.php?id=1&name=小明'),
axios.get('http://localhost/axiosdemo/getapi.php?id=2&name=小红')
]).then(
axios.spread((res1,res2)=>{ // axios方法,又可以将结果分开处理
console.log(res1);
console.log(res2);
console.log(res3);
})
).catch(err => {
console.log(错误是'+err)
});
1.基本语法
》做完全局配置之后在发送axios请求时就简单了
axios.default.baseURL="http://127.0.0.1";
axios.default.timeout=5000;
axios.default.headers.post['content-type']='application/x-www-form-urlencoded';
》整理数据
axios.defaults.transformRequest = function (data){
data = JSON.stringify(data);
return data;
);
axios.defaults.baseURL = 'http://localhost/axiosdemo/';
axios.defaults.timeout = 5000;
// get请求
axios.get('getapi.php?id=1').then(res=>{
console.log(res)
}).catch(err => {
console.log('错误是'+err)
});
// post请求
axios.post('getapi.php',"id=2&name=小明").then(res=>{
console.log(res)
}).catch(err => {
console.log('错误是'+err)
});
1.基本语法
》有时候后台接口地址有多个并且超时时长不一样,我们不可能在axios中把每个后台请求的域名地址都拼接在URl中, 并且在axios中的config写不同的超时时长,很繁琐,这个时候可以用到axios实例,在实例中可以配置这两种参数。
》假如新建了一个axios实例但是没有参数,取得就是全局的配置值,实例中如果有则优先取实例中的
》axios实例的相关配置(config参数)
baseURL:请求的域名基本地址(如: http://localhost:8080)
timeout:后端定义的超时时长(默认是1000ms)
url:请求的路径(如:/data.json)
method:精求方法(get、post...headers:设置请求头
params:请求的参数拼接在url中
data:请求的参数放在request body中
2.使用实例
let work = axios.create({
baseURL:'http://localhost/axiosdemo/',
timeout:5000
});
// post请求
work.post('getapi.php',"id=2&name=小明").then(res=>{
console.log(res)
}).catch(err => {
console.log('错误是'+err)
});
// get请求
work.get('getapi.php?id=2&name=小明').then(res=>{
console.log(res)
}).catch(err => {
console.log('错误是'+err)
});
// get请求2, options访问
work({
url:'getapi.php?id=3&name=小刚'
}).then(res=>{
console.log(res)
}).catch(err => {
console.log('错误是'+err)
});
1.基本语法
请求拦截器、响应拦截器,可以在请求服务器数据时,进行其它的显示
》为每个请求都带上的参数,比如token,时间戳等。
》对返回的状态进行判断,比如token是否过期
2.使用实例
//1.请求拦截器
//每次发送诘求之前判断是否存在token
//如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
//即使本地存在token,也有可能oken是过期的,所以在响应拦截器中要对返回状态进行判断
axios.interceptors.request.use(config =>{
const token = window.localStorage.getItem("token");
token && (config.headers.Authorization = token);
return config
}, error=>{
return Promise.error(error);
});
//2.响应拦截器
//如果返回的状态码为200,说明接口请求成功,可以正常拿到数据,否则的话抛出错误
//服务器状态码不是2开头的的情况
//这里可以眼你们的后台开发人员协商好统一的错误状杰码
//然后根据返回的达态码进行一些操作,例如登录过期提示,错误提示等等
axios.interceptors.response.use(response =>{
if (response.status === 200){
return Promise.resolve(response);
}else {
return Promise.reject(response);
}
},error => {
if (error.response.status){
return Promise.reject(error.response);
}
});