组件通信 父子组件/组件的嵌套
组件与组件之间是可以相互嵌套 环环相扣 那么这种关系我们就称之为 父子组件
父子组件的作用域
组件与组件之间是一个完整的独立的个体 他们之间的数据默认是不能相互使用
组件通信/组件传值
正向传值–父给子传值–props
props是一个属性 他可以接收组件外部的传值
语法:写在 data methods watch computed components 同级
props :[ 接收的变量1,接收的变量2,,,,,,,n]
子组件设置接收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <template > <div > zizizizizizzz--{{ziprops}} </div > </template > <script > export default { props :["ziprops" ] } </script > <style > </style >
父组件开始传递
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 <template > <div > fufufufufufuffuf <Zi v-bind:ziprops ="futext" /> </div > </template > <script > import Zi from "./zi.vue" export default { components :{ Zi }, data ( ){ return { futext :"我是父组件的变量么么哒!!!!" } } } </script > <style > </style >
思考
我使用props完成正向传值 那么我父组件给子组件是不是可以传递任何数据类型的内容?
正向传值默认的时候可以传递任何的数据类型
props验证
因为默认情况下props可以接收任何数据类型的数据 子组件在接收数据之后处理可能会有一些问题
那么我们就可以在接收props的时候对数据进行验证 限制传递进来的数据格式与类型
语法:
props:{
你接受的props变量:数据类型
你接受的props变量:[数据类型1,数据类型2]
}
注意 props验证不会影响我们的显示结果 而是只会在控制台给我们开发者一个警告提示我们有问题
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 > zizizizizizzz--{{ziprops+1}} </div > </template > <script > export default { props :{ ziprops :{ type :Number , required :true } } } </script > <style > </style >
逆向传值–子给父传值
逆向传值默认是不被允许的(vue中没有专门的语法进行逆向传值)我们需要使用其他的方式来侧面的完成
$emit(自定事件名,数据) 自定义事件来完成
1.子组件通过事件(必须要通过事件触发才能传递)抛出一个自定义事件
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 <template > <div > zizizizizizzi <button @click ="fun()" > 点我逆向传值</button > </div > </template > <script > export default { data ( ){ return { zitext :"我是子组件的数据!!!!" } }, methods :{ fun ( ){ this .$emit("ziemit" ,this .zitext ) } } } </script > <style > </style >
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 <template > <div > fufufuffufufufu <Zi @ziemit ="fun" /> </div > </template > <script > import Zi from "./zi.vue" export default { methods :{ fun (val ){ console .log ("我是父组件" ,val) } }, components :{ Zi } } </script > <style > </style >
ref逆向传值
ref绑定到组件之上就可以回去到子组件这个vueComponent组件 从而可以得到当前子组件的所有信息 已达到逆向传值的目的
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 <template > <div > fu <Zi @ziemit ="fun" ref ="zi" /> </div > </template > <script > import Zi from "@/components/zi.vue" export default { components :{ Zi }, methods :{ fun (val ){ console .log ("我是父组件" ,val); console .log (this .$refs .zi ); } } } </script > <style > </style >
同胞传值–兄弟组件传值
同胞传值使用—中央事件总线(eventbus)
中央事件总线 就是凌驾在两个兄弟组件之上的一个空的vue实例 通过这个空的vue实例建立一个在兄弟组件传递数据的桥梁
1.创建一个eventbus的文件夹用来容纳中央事件总线 并且创建
1 2 3 import Vue from "vue" export default new Vue
2.在组件中抛出数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <template > <div > aaaaaaaaaaaaaaaaaaaaa <button @click ="fun()" > 点我传递数据</button > </div > </template > <script > import eventbus from "@/eventbus" export default { methods :{ fun ( ){ eventbus.$emit("zia" ,"我是zia的数据么么哒" ) } } } </script > <style > </style >
3.在接收的组件中使用数据
$on() 监听实例上的自定义事件
$on(“你要监听的自定义事件”,(就是自定义事件的参数)=>{})
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 <template > <div > bbbbbbbbbbbbbbbbbbb---{{text}} </div > </template > <script > import eventbus from "@/eventbus" export default { data ( ){ return { text :"" } }, mounted ( ){ eventbus.$on("zia" ,(val )=> { console .log (val) this .text =val }) } } </script > <style > </style >
跨层级传值–爷爷给孙子
vuex—统一状态(数据)管理工具 通过vuex可以把整个项目的数据进行统一的存储 不管是哪个组件用 只需要这个组件去vuex的仓库里面拿就可以了 不需要传统组件传值的那些复杂度
因为组件数据传递是单向数据流
vuex脚手架创建
在下载项目的时候 选中vuex即可
store文件夹 就是用来存放vuex的内容
vuex state
state就是数据源 存放vuex数据的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import Vue from 'vue' import Vuex from 'vuex' Vue .use (Vuex )export default new Vuex .Store ({ state : { text :"字符串" , num :6666 , bool :true , arr :[111 ,2222 ,3333 ,444 ], obj :{ name :"xixix" } }, mutations : { }, actions : { }, modules : { } })
使用方式1
在你想用的地方直接使用 this.$store.state.xxx
1 2 3 4 5 <template > <div class ="about" > <h1 > This is an about page----{{this.$store.state.num}}</h1 > </div > </template >
使用方式2
使用计算属性来读取state中的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <template > <div class ="about" > <h1 > This is an about page----{{this.$store.state.num}}</h1 > {{newnum}} </div > </template > <script > export default { computed :{ newnum ( ){ return this .$store .state .num } } } </script >
vuex mutations
vuex中不能直接使用=给数据修改值
vuex中修改数据 必须使用mutations(是一个属性 在它里面是一个个的修改方法)
如果在页面中要触发mutations 那么 使用this.$store.commit()来进行触发
组件内使用commit来触发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <template > <div > <h1 > 修改vuex的数据---{{ this.$store.state.num }}</h1 > <button @click ="fun()" > 点我修改num</button > </div > </template > <script > export default { methods : { fun ( ) { this .$store .commit ("xiaoming" , { text : "我是数据" }); }, }, }; </script > <style > </style >
vuex中创建对应的mutations
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 import Vue from 'vue' import Vuex from 'vuex' Vue .use (Vuex )export default new Vuex .Store ({ state : { text :"字符串" , num :6666 , bool :true , arr :[111 ,2222 ,3333 ,444 ], obj :{ name :"xixix" } }, mutations : { xiaoming (state,payload ){ state.num =payload.text } }, actions : { }, modules : { } })
扩展–vuex数据修改刷新丢失问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 created () { if (sessionStorage.getItem ("store" ) ) { this .$store .replaceState (Object .assign ({}, this .$store .state ,JSON .parse (sessionStorage.getItem ("store" )))) } window .addEventListener ("beforeunload" ,()=> { sessionStorage.setItem ("store" ,JSON .stringify (this .$store .state )) }) },
vuex actions
他是vuex中的一个异步触发器 他的作用就是触发异步操作的
页面内通过dispatch()来触发action进行异步操纵的触发器 actions中就是一个个的方法 每个方法就是一个异步操纵
1.组件内使用dispatch触发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <template > <div class ="home" > <button @click ="fun()" > 点我进行异步请求</button > </div > </template > <script > export default { name : 'Home' , methods :{ fun ( ){ this .$store .dispatch ("xiaoming" ,{key :"我是数据" }) } } } </script >
2.在vuex中来创建actions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import Vue from 'vue' import Vuex from 'vuex' Vue .use (Vuex )export default new Vuex .Store ({ state : {}, mutations : {}, actions : { xiaoming ( ) { console .log ("我是触发异步操作的地方" ); } }, modules : {} })
vuex getters
他就是vuex的计算属性
计算属性 对data中的数据进行处理并且返回新的处理之后的结果 仅仅只能对当前组件生效
getters 作为vuex的计算属性 他是对state中的数据进行处理并且返回新的结果 他处理的数据可以在任何组件中进行使用
语法:
getters:{
处理的新变量() {
return
}
}
编写getters
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import Vue from 'vue' import Vuex from 'vuex' import phonem from "./module/phonem.js" Vue .use (Vuex )export default new Vuex .Store ({ state : { text :"abcdefghijk" }, mutations : { }, actions : { }, getters :{ newtext (state ){ return state.text .toUpperCase () } }, modules : { phonem } })
读取的时候
this.$store.getters.xxxx
1 <h1 > {{this.$store.getters.newtext}}</h1 >
vuex modules
通过模块 就是把原来写在一堆堆的vuex内容 分别拆分成一个个的小文件小模块 方便后期的维护与管理
1.在store文件夹下新建modules文件夹用来容纳模块
2.在模块文件中写入相关vuex属性(原来怎么写在模块中就怎么写)
1 2 3 4 5 6 7 8 9 10 11 12 13 let commitm={ state :{ num :987 }, mutations : { xiaoming (state,payload ){ state.num =payload.text } }, } export default commitm
3.在vuex的文件中 引用使用模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import Vue from 'vue' import Vuex from 'vuex' Vue .use (Vuex )import homem from "./modules/homem.js" import aboutm from "./modules/aboutm.js" import commitm from "./modules/commitm.js" export default new Vuex .Store ({ modules : { homem, aboutm, commitm } })
4.在页面中使用的时候因为使用了模块 所以在读取数据的时候就必须是 this.$store.state.模块名.xxxx
vuex的流程
我们把vuex进行modules的拆分 然后再组件中使用dispatch()来触发actions进行异步的触发 在其中进行数据请求 把请求来得数据 要通过 commit来调用mutations进行state的数据修改 从而在页面可以读取请求过来得state数据 同时我们也可以对state的数据进行计算属性的getters处理 已达到一条数据在不同位置可以展示不同的形态
前后端数据交互 前端后端
前端 数据展示
后端 数据处理
方式
原生方式
jqeury ajax
axios 是一个第三方的数据请求库 他是基于promise 二次封装XHR对象的一个数据请求库
1 下载:
npm install –save axios
2 在要使用的地方引用
import 起个名字 from “axios”
3.开始发送
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 <template > <div > <h1 > 初级写法</h1 > <button @click ="fun()" > 点我请求数据</button > </div > </template > <script > import $http from "axios" export default { methods :{ fun ( ){ $http({ url :"http://www.weather.com.cn/data/cityinfo/101320101.html" , method :"get" , }).then ((ok )=> { cosnole.log (ok) }).catch ((err )=> { console .log (err) }) } } } </script > <style > </style >
数据请求封装与拦截器
拦截器
每次发送请求或者请求响应的时候 都会经过拦截器 才会进入到我们的程序(就是对我们的请求和响应进行发送前或者获取前的一个拦截 )
请求拦截
发送请求的时候会经过请求拦截 我们就可以在请求拦截上携带每次都要给后台的数据(用户的登录状态)
响应拦截
每次相应数据的时候都会经过响应拦截 (我们就可以在响应拦截的时候对我们的错误或者成功做出反应)
编写拦截器
1.在src下新建一个util文件夹(工具文件夹用来放置一些项目中辅佐工具库)再创建对应的文件用来容纳拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import axios from "axios" let service=axios.create ()service.interceptors .request .use (function (config ) { return config; }, function (error ) { return Promise .reject (error); }); service.interceptors .response .use (function (response ) { return response; }, function (error ) { return Promise .reject (error); }); export default service
数据请求的封装
尽量符合最新的es标准promise来进行封装
1.在src下新建一个api的文件夹(就是容纳数据请求的)新建一个文件容纳封装的请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import service from "@/utils/service.js" let getlink =(url,method='get' )=>{ return new Promise ((resolve,reject )=> { service.request ({ url, method }).then ((ok )=> { resolve (ok) }).catch ((err )=> { reject (err) }) }) } export default getlink
使用封装的请求
1.在你想用的地方先引用 在使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <template > <div class ="about" > <h1 > This is an about page</h1 > </div > </template > <script > import getlink from "@/api/getapi.js" ;export default { created ( ){ getlink ("/api/data/cityinfo/101320101.html" ).then ((ok )=> { console .log (ok) }).catch ((err )=> { console .log (err) }) } }; </script >
跨域 因为浏览器的安全机制 同源策略 不同端口不同域名不同协议 就会造成跨域
jsonp
代理跨域
代理:造成跨域的问题是浏览器的安全机制 因为有了这个安全机制我们才要解决跨域
我现在不让浏览器帮我发送请求了 而是让我项目的服务器帮助我绕开浏览器发送请求
nginx反向代理
devServer代理跨域
devServer就是vue脚手架中那个内置的微型开发小服务器
1.在项目的根路径下 创建一个vue.config.js
2.写入如下内容
1 2 3 4 5 6 7 8 9 10 11 12 module .exports ={ devServer : { proxy : { '/api' : { target : 'http://www.weather.com.cn/' , pathRewrite : { '^/api' : '' } }, } }, }
3.修改请求路径
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 > 初级写法</h1 > <button @click ="fun()" > 点我请求数据</button > </div > </template > <script > import $http from "axios" export default { methods :{ fun ( ){ $http({ url :"/api/data/cityinfo/101320101.html" , method :"get" , }).then ((ok )=> { console .log (ok.data .weatherinfo ) }).catch ((err )=> { console .log (err) }) } } } </script > <style > </style >
4.千万不要忘了重启
动态组件 动态(自动变化 动态变化)组件
多个组件只有一个挂载点并且动态切换
语法
1.引用调用和之前使用组件是一样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <template > <div > </div > </template > <script > import One from "@/components/one.vue" import Two from "@/components/two.vue" import Three from "@/components/three.vue" export default {components :{One ,Two ,Three ,} } </script > <style > </style >
3.使用 就需要使用动态组件的语法
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 <template > <div > <h1 > 动态组件 ---- 多个组件使用同一个挂载点</h1 > <button @click ="el='One'" > 点我去one</button > <button @click ="el='Two'" > 点我去two</button > <button @click ="el='Three'" > 点我去three</button > <component :is ="el" > </component > </div > </template > <script > import One from "@/components/one.vue" import Two from "@/components/two.vue" import Three from "@/components/three.vue" export default { data ( ){ return { el :"Three" } }, components :{ One , Two , Three , } } </script > <style > </style >
动态组件或者路由切换的时候组件状态(内容数据)丢失问题
在上面的例子中如果我们在每个组件总添加一个输入框 并且输入内容 大家会发现当我们切换动态组件 或者切换路由的时候 组件中的状态内容会丢失
原理:
因为当我们每次切换组件的时候 vue都会创建一个新的实例
keep-alive keep-alive可以用来保存组件中的状态内容
语法:
你想要保存状态的内容
如果要保存动态组件的状态
1 2 3 4 <keep-alive > <component :is ="el" > </component > </keep-alive >
如果要保存路由的状态 只需要用keep-alive包裹路由出口即可
1 2 3 <keep-alive > <router-view /> </keep-alive >
扩展keep-alive的属性与钩子
属性
在vue2.1以后 keep-alive就给我们添加了两个新的属性
include 你要缓存谁
1 2 3 <keep-alive include ="One" > <component :is ="el" > </component > </keep-alive >
exclude你不想缓存谁
1 2 3 <keep-alive exclude ="Two" > <component :is ="el" > </component > </keep-alive >
如果我同时写了include与exclude怎么办?先执行谁?
exclude的优先级大于include
钩子函数
activated
在进入被keep-alive管理的组件的时候触发
deactivated
在离开被keep-alive管理的组件的时候触发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <template > <div > 我是机票的组件 <input type ="text" > <input type ="text" > <input type ="text" > </div > </template > <script > export default {activated ( ){console .log ("进入被keep-alive管理的组件时候生效" )}, deactivated ( ){console .log ("离开被keep-alive管理的组件时候生效" )} } </script > <style > </style >
nextTick原理与使用场景 Vue中数据修改之后,页面的dom不是立即改变更新 的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <template > <div class ="about" > <h1 > vue中数据修改之后,页面的dom不是立即修改更新的</h1 > <h2 ref ="demoh" > {{text}}</h2 > <button @click ="fun()" > 点我修改数据</button > </div > </template > <script > export default { data ( ){ return { text :"我是数据" } }, methods :{ fun ( ){ this .text ="我变了" console .log (this .$refs .demoh .innerText ); } } } </script >
vue中的异步说明
简单的来说 vue在修改数据之后 视图是不会立刻更新数据的 而是等待着所有的数据都修改完成之后 然后再统一的进行数据的更新
原理流程:
1.vue修改数据是一个同步任务 这些同步任务都在线程的主轴上进行 形成了一个执行栈 但是在这个过程中并没有进行dom的操纵
2.vue会开启一个异步的列队 把所有修改之后的结果缓存起来
3.同步修改的任务执行完了 这个时候才开始进行dom的更新
问题
那么既然vue在修改完数据之后dom不是立即更新 那么我们如何得到修改完之后的dom内容???
nextTick
就是等待dom加载完毕之后立即执行
语法:
nextTick(()=>{
dom加载完毕之后立即执行
})
应用场景
1.我想在created中得到dom的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <template > <div > <h1 ref ="demoh" > 我是内容</h1 > </div > </template > <script > export default { created ( ){ this .$nextTick(()=> { console .log (this .$refs .demoh .innerText ); }) } } </script > <style > </style >
场景2.我想在修改完数据之后 立即得到改变之后的dom内容
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 <template > <div class ="about" > <h1 > vue中数据修改之后,页面的dom不是立即修改更新的</h1 > <h2 ref ="demoh" > {{text}}</h2 > <button @click ="fun()" > 点我修改数据</button > </div > </template > <script > export default { data ( ){ return { text :"我是数据" } }, methods :{ fun ( ){ this .text ="我变了" this .$nextTick(()=> { console .log (this .$refs .demoh .innerText ); }) } } } </script >
项目的打包 1.使用npm run build来实现打包 会发现项目的根路径下出现了一个dist文件夹 (就是打包之后的内容)
但是我们直接运行这个文件夹下的index.html会发现页面什么都没有而且出现了很多报错
1 2 3 4 5 6 7 8 9 10 11 Refused to apply style from 'http://127.0.0.1:5500/css/app.5c9713c3.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled. app.7ab044df.js:1 Failed to load resource: the server responded with a status of 404 (Not Found) chunk-vendors.9ea14b12.js:1 Failed to load resource: the server responded with a status of 404 (Not Found) index.html:29 Live reload enabled. about.74c28bb4.js:1 Failed to load resource: the server responded with a status of 404 (Not Found) :5500/favicon.ico:1 Failed to load resource: the server responded with a status of 404 (Not Found)
原因是因为打包之后默认找不到打包的静态资源路径
2.修改项目的静态资源路径
在项目的根路径下创建一个vue.config.js文件,写入如下内容
1 2 3 4 module .exports = { publicPath : "./" , }
3.修改路由模式为hash模式
1 2 3 4 5 const router = new VueRouter ({ mode : 'hash' , base : process.env .BASE_URL , routes })