VUE3 更新:
vue3.0取消了2.0部分api,所以路由跳转传值方式有所不同。
这里主要讲编程式导航,也就是router.push(location, onComplete?, onAbort?)
vue3.0新增API:useRouter和useRoute
一.路由跳转
1.首先在需要跳转的页面引入API—useRouter
1 |
import { useRouter } from 'vue-router' |
2.在跳转页面定义router变量
1 2 |
//首先在setup中定义 const router = useRouter() |
3.用router.push跳转页面
1 2 3 4 5 6 7 8 9 10 11 |
// 字符串 router.push('home') // 对象 router.push({ path: 'home' }) // 命名的路由 router.push({ name: 'user', params: { userId: '123' }}) // 带查询参数,变成 /register?userId=123 router.push({ path: 'register', query: { userId: '123' }}) |
4.如果有参数的话,在接收页面引入API–useRoute
1 |
import { useRoute } from 'vue-router' |
5.在接收页面定义变量route,获取传过来的变量
1 2 3 4 5 6 7 |
//首先在setup中定义 const route = useRoute() //query let userId=route.query.userId; //params let userId=route.params.userId; |
二.页面传参的问题,此处有坑,特别注意
1.如果提供了 path
,params
会被忽略,但query
没有这种情况,此时需要提供路由的 name
或手写完整的带有参数的 path
1 2 3 4 5 |
const userId = '123' router.push({ name: 'user', params: { userId }}) // -> /user/123 router.push({ path: `/user/${userId}` }) // -> /user/123 // 这里的 params 不生效 router.push({ path: '/user', params: { userId }}) // -> /user |
2.上述规则同样适用于 router-link 组件的 to 属性
3.如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个 /users/1 -> /users/2),你需要使用 beforeRouteUpdate 来响应这个变化 (比如抓取用户信息)
4.如果上述写法出现 prams 传参失效和报错问题,原因如下:
vue-router 在4.1.4版本有一个更新:删除了没有定义在path里的params。
mportant Note
Changes introduced by e8875705eb8b8a0756544174b85a1a3c2de55ff6.
If you were relying on passing params
that were not defined as part of the path
, eg: having a route defined as follows:
1 2 3 4 |
<span class="pl-kos">{</span> <span class="pl-c1">path</span>: <span class="pl-s">'/somewhere'</span><span class="pl-kos">,</span> <span class="pl-c1">name</span>: <span class="pl-s">'somewhere'</span> <span class="pl-kos">}</span> |
And pushing with an artificial param:
1 |
<span class="pl-s1">router</span><span class="pl-kos">.</span><span class="pl-en">push</span><span class="pl-kos">(</span><span class="pl-kos">{</span> <span class="pl-c1">name</span>: <span class="pl-s">'somewhere'</span><span class="pl-kos">,</span> <span class="pl-c1">params</span>: <span class="pl-kos">{</span> <span class="pl-c1">oops</span>: <span class="pl-s">'gets removed'</span> <span class="pl-kos">}</span> <span class="pl-kos">}</span><span class="pl-kos">)</span> |
This change will break your app. This behavior has worked in some scenarios but has been advised against for years as it’s an anti-pattern in routing for many reasons, one of them being reloading the page lose the params. Fortunately, there are multiple alternatives to this anti-pattern:
-
Putting the data in a store like pinia: this is relevant if the data is used across multiple pages
-
Move the data to an actual param by defining it on the route’s
path
or pass it asquery
params: this is relevant if you have small pieces of data that can fit in the URL and should be preserved when reloading the page -
Pass the data as
state
to save it to the History API state:1234<<span class="pl-ent">router-link</span> :<span class="pl-e">to</span>=<span class="pl-s1"><span class="pl-pds">"</span>{ name<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>somewhere<span class="pl-pds">'</span></span>, state<span class="pl-k">:</span> { myData } }<span class="pl-pds">"</span></span>>...</<span class="pl-ent">router-link</span>><<span class="pl-ent">button</span>@<span class="pl-e">click</span>=<span class="pl-s1"><span class="pl-pds">"</span><span class="pl-smi">$router</span>.<span class="pl-c1">push</span>({ name<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>somewhere<span class="pl-pds">'</span></span>, state<span class="pl-k">:</span> { myData } })<span class="pl-pds">"</span></span>>...</<span class="pl-ent">button</span>>Note
state
is subject to History state limitations. -
Pass it as a new property to
to.meta
during navigation guards:123456<span class="pl-s1">router</span><span class="pl-kos">.</span><span class="pl-en">beforeEach</span><span class="pl-kos">(</span><span class="pl-k">async</span> <span class="pl-s1">to</span> <span class="pl-c1">=></span> <span class="pl-kos">{</span><span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">to</span><span class="pl-kos">.</span><span class="pl-c1">meta</span><span class="pl-kos">.</span><span class="pl-c1">shouldFetch</span><span class="pl-kos">)</span> <span class="pl-kos">{</span><span class="pl-c">// name `data` whatever you want</span><span class="pl-s1">to</span><span class="pl-kos">.</span><span class="pl-c1">meta</span><span class="pl-kos">.</span><span class="pl-c1">data</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-en">fetchSomething</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">}</span><span class="pl-kos">}</span><span class="pl-kos">)</span>This is known an transient state and since it’s in a navigation guard, it will be preserved when reloading the page. Check the documentation for more details.
Fixing #1497, required getting rid of unused params and therefore will broke this long standing anti-pattern usage
——————————————————————————————-
vue组件传值和页面间通信传参多种方式,在vue开发时大部分一个页面内父子间组件传参,第二页是跳路由页面间传参,下面解决几种情况不同传参数方法
一、通过路由带参数进行传值
①两个组件 A和B,A组件通过query把orderId传递给B组件(触发事件可以是点击事件、钩子函数等)
1 |
this.$router.push({ path: '/conponentsB', query: { orderId: 123 } }) // 跳转到B |
②在B组件中获取A组件传递过来的参数
1 |
this.$route.query.orderId |
二、通过设置 Session Storage缓存的形式进行传递
①两个组件A和B,在A组件中设置缓存orderData
1 |
const orderData = { 'orderId': 123, 'price': 88 } sessionStorage.setItem('缓存名称', JSON.stringify(orderData)) |
此时 dataB 就是数据 orderData
朋友们可以百度下 Session Storage(程序退出销毁) 和 Local Storage(长期保存) 的区别。
三、父子组件之间的传值,用props
(一)父组件往子组件传值props
①定义父组件,父组件传递 number这个数值给子组件,如果传递的参数很多,推荐使用json数组{}的形式
②定义子组件,子组件通过 props方法获取父组件传递过来的值。props中可以定义能接收的数据类型,如果不符合会报错。
当然也可以简单一点,如果不考虑数据类型,直接 props:[“number”,”string”]就可以了,中括号包裹,多个值使用,分隔。
③假如接收的参数 是动态的,比如 input输入的内容 v-model的形式
注意:传递的参数名称 支持驼峰命名,下图 描述不正确(1.0是不支持的)
④父子组件传值,数据是异步请求,有可能数据渲染时报错
原因:异步请求时,数据还没有获取到但是此时已经渲染节点了
解决方案:可以在 父组件需要传递数据的节点加上 v-if = false,异步请求获取数据后,v-if = true
(二)、子组件往父组件传值,通过emit事件
四、不同组件之间传值,通过eventBus(小项目少页面用eventBus,大项目多页面使用 vuex)
①定义一个新的vue实例专门用于传递数据,并导出
②定义传递的方法名和传输内容,点击事件或钩子函数触发eventBus.emit事件
③接收传递过来的数据
注意:enentBus是一个另一个新的Vue实例,区分两个this所代表得vue实例
五、vuex进行传值
为什么使用vuex?
vuex主要是是做数据交互,父子组件传值可以很容易办到,但是兄弟组件间传值(兄弟组件下又有父子组件),或者大型spa单页面框架项目,页面多并且一层嵌套一层的传值,异常麻烦,用vuex来维护共有的状态或数据会显得得心应手。
需求:两个组件A和B,vuex维护的公共数据是 餐馆的名称 resturantName,默认餐馆名称是 飞歌餐馆,那么现在A和B页面显示的就是飞歌餐馆。如果A修改餐馆名称 为 A餐馆,则B页面显示的将会是 A餐馆,反之B修改同理。这就是vuex维护公共状态或数据的魅力,在一个地方修改了数据,在这个项目的其他页面都会变成这个数据。
①使用 vue-cli脚手架工具创建一个工程项目,工程目录,创建组件A和组件B路由如下:
路由如下:
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 |
<span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span> <span class="hljs-keyword">import</span> Router <span class="hljs-keyword">from</span> <span class="hljs-string">'vue-router'</span> <span class="hljs-keyword">import</span> componentsA <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/componentsA'</span> <span class="hljs-keyword">import</span> componentsB <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/componentsB'</span> Vue.use(Router) <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">new</span> Router({ <span class="hljs-attr">mode</span>: <span class="hljs-string">'history'</span>, <span class="hljs-attr">routes</span>: [ { <span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'componentsA'</span>, <span class="hljs-attr">component</span>: componentsA }, { <span class="hljs-attr">path</span>: <span class="hljs-string">'/componentsA'</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'componentsA'</span>, <span class="hljs-attr">component</span>: componentsA }, { <span class="hljs-attr">path</span>: <span class="hljs-string">'/componentsB'</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'componentsB'</span>, <span class="hljs-attr">component</span>: componentsB } ] }) |
app.vue
②开始使用vuex,新建一个 sotre文件夹,分开维护 actions mutations getters
②在store/index.js文件中新建vuex 的store实例
*as的意思是 导入这个文件里面的所有内容,就不用一个个实例来导入了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span> <span class="hljs-keyword">import</span> Vuex <span class="hljs-keyword">from</span> <span class="hljs-string">'vuex'</span> <span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> getters <span class="hljs-keyword">from</span> <span class="hljs-string">'./getters'</span> <span class="hljs-comment">// 导入响应的模块,*相当于引入了这个组件下所有导出的事例</span> <span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> actions <span class="hljs-keyword">from</span> <span class="hljs-string">'./actions'</span> <span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> mutations <span class="hljs-keyword">from</span> <span class="hljs-string">'./mutations'</span> Vue.use(Vuex) <span class="hljs-comment">// 首先声明一个需要全局维护的状态 state,比如 我这里举例的resturantName</span> <span class="hljs-keyword">const</span> state = { <span class="hljs-attr">resturantName</span>: <span class="hljs-string">'飞歌餐馆'</span> <span class="hljs-comment">// 默认值</span> <span class="hljs-comment">// id: xxx 如果还有全局状态也可以在这里添加</span> <span class="hljs-comment">// name:xxx</span> } <span class="hljs-comment">// 注册上面引入的各大模块</span> <span class="hljs-keyword">const</span> store = <span class="hljs-keyword">new</span> Vuex.Store({ state, <span class="hljs-comment">// 共同维护的一个状态,state里面可以是很多个全局状态</span> getters, <span class="hljs-comment">// 获取数据并渲染</span> actions, <span class="hljs-comment">// 数据的异步操作</span> mutations <span class="hljs-comment">// 处理数据的唯一途径,state的改变或赋值只能在这里</span> }) <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> store <span class="hljs-comment">// 导出store并在 main.js中引用注册。</span> |
③actions
1 2 3 4 5 6 7 8 9 10 |
<span class="hljs-comment">// 给action注册事件处理函数。当这个函数被触发时候,将状态提交到mutations中处理</span> <span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">modifyAName</span>(<span class="hljs-params">{commit}, name</span>) </span>{ <span class="hljs-comment">// commit 提交;name即为点击后传递过来的参数,此时是 'A餐馆'</span> <span class="hljs-keyword">return</span> commit (<span class="hljs-string">'modifyAName'</span>, name) } <span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">modifyBName</span>(<span class="hljs-params">{commit}, name</span>) </span>{ <span class="hljs-keyword">return</span> commit (<span class="hljs-string">'modifyBName'</span>, name) } <span class="hljs-comment">// ES6精简写法</span> <span class="hljs-comment">// export const modifyAName = ({commit},name) => commit('modifyAName', name)</span> |
④mutations
1 2 3 4 5 6 7 |
<span class="hljs-comment">// 提交 mutations是更改Vuex状态的唯一合法方法</span> <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> modifyAName = <span class="hljs-function">(<span class="hljs-params">state, name</span>) =></span> { <span class="hljs-comment">// A组件点击更改餐馆名称为 A餐馆</span> state.resturantName = name <span class="hljs-comment">// 把方法传递过来的参数,赋值给state中的resturantName</span> } <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> modifyBName = <span class="hljs-function">(<span class="hljs-params">state, name</span>) =></span> { <span class="hljs-comment">// B组件点击更改餐馆名称为 B餐馆</span> state.resturantName = name } |
⑤getters
1 2 |
<span class="hljs-comment">// 获取最终的状态信息</span> <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> resturantName = <span class="hljs-function"><span class="hljs-params">state</span> =></span> state.resturantName |
⑥在main.js中导入 store实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// The Vue build version to <span class="hljs-keyword">load</span> <span class="hljs-keyword">with</span> the <span class="hljs-string">`import`</span> command // (runtime-<span class="hljs-keyword">only</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">standalone</span>) has been <span class="hljs-keyword">set</span> <span class="hljs-keyword">in</span> webpack.base.conf <span class="hljs-keyword">with</span> an alias. <span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span> <span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App'</span> <span class="hljs-keyword">import</span> router <span class="hljs-keyword">from</span> <span class="hljs-string">'./router'</span> <span class="hljs-keyword">import</span> <span class="hljs-keyword">store</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'./store'</span> Vue.config.productionTip = <span class="hljs-literal">false</span> <span class="hljs-comment">/* eslint-disable no-new */</span> <span class="hljs-keyword">new</span> Vue({ el: <span class="hljs-string">'#app'</span>, router, <span class="hljs-keyword">store</span>, // 这样就能全局使用vuex了 components: { App }, <span class="hljs-keyword">template</span>: <span class="hljs-string">'<App/>'</span> }) |
④在组件A中,定义点击事件,点击 修改 餐馆的名称,并把餐馆的名称在事件中用参数进行传递。
…mapactions 和 …mapgetters都是vuex提供的语法糖,在底层已经封装好了,拿来就能用,简化了很多操作。
其中…mapActions([‘clickAFn’]) 相当于this.$store.dispatch(‘clickAFn’,{参数}),mapActions中只需要指定方法名即可,参数省略。
…mapGetters([‘resturantName’])相当于this.$store.getters.resturantName
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<span class="hljs-tag"><<span class="hljs-name">template</span>></span> <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"componentsA"</span>></span> <span class="hljs-tag"><<span class="hljs-name">P</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>></span>组件A<span class="hljs-tag"></<span class="hljs-name">P</span>></span> <span class="hljs-tag"><<span class="hljs-name">P</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"titleName"</span>></span>餐馆名称:{{resturantName}}<span class="hljs-tag"></<span class="hljs-name">P</span>></span> <span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-comment"><!-- 点击修改 为 A 餐馆 --></span> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"modifyAName('A餐馆')"</span>></span>修改为A餐馆<span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"marTop"</span>></span> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"trunToB"</span>></span>跳转到B页面<span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span> <span class="hljs-tag"></<span class="hljs-name">template</span>></span> ##script import {mapActions, mapGetters} from 'vuex' export default { name: 'A', data () { return { } }, methods:{ ...mapActions( // 语法糖 ['modifyAName'] // 相当于this.$store.dispatch('modifyName'),提交这个方法 ), trunToB () { this.$router.push({path: '/componentsB'}) // 路由跳转到B } }, computed: { ...mapGetters(['resturantName']) // 动态计算属性,相当于this.$store.getters.resturantName } } ##script# <span class="hljs-comment"><!-- Add "scoped" attribute to limit CSS to this component only --></span> <span class="hljs-tag"><<span class="hljs-name">style</span> <span class="hljs-attr">scoped</span>></span><span class="css"> <span class="hljs-selector-class">.title</span>,<span class="hljs-selector-class">.titleName</span>{ <span class="hljs-attribute">color</span>: blue; <span class="hljs-attribute">font-size</span>: <span class="hljs-number">20px</span>; } <span class="hljs-selector-class">.btn</span>{ <span class="hljs-attribute">width</span>: <span class="hljs-number">160px</span>; <span class="hljs-attribute">height</span>: <span class="hljs-number">40px</span>; <span class="hljs-attribute">background-color</span>: blue; <span class="hljs-attribute">border</span>: none; <span class="hljs-attribute">outline</span>: none; <span class="hljs-attribute">color</span>: <span class="hljs-number">#ffffff</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>; } <span class="hljs-selector-class">.marTop</span>{ <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">20px</span>; } </span><span class="hljs-tag"></<span class="hljs-name">style</span>></span> |
B组件同理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<span class="hljs-tag"><<span class="hljs-name">template</span>></span> <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"componentsB"</span>></span> <span class="hljs-tag"><<span class="hljs-name">P</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>></span>组件B<span class="hljs-tag"></<span class="hljs-name">P</span>></span> <span class="hljs-tag"><<span class="hljs-name">P</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"titleName"</span>></span>餐馆名称:{{resturantName}}<span class="hljs-tag"></<span class="hljs-name">P</span>></span> <span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-comment"><!-- 点击修改 为 B 餐馆 --></span> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"modifyBName('B餐馆')"</span>></span>修改为B餐馆<span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"marTop"</span>></span> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"trunToA"</span>></span>跳转到A页面<span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span> <span class="hljs-tag"></<span class="hljs-name">template</span>></span> ##script## import {mapActions, mapGetters} from 'vuex' export default { name: 'B', data () { return { } }, methods:{ ...mapActions( // 语法糖 ['modifyBName'] // 相当于this.$store.dispatch('modifyName'),提交这个方法 ), trunToA () { this.$router.push({path: '/componentsA'}) // 路由跳转到A } }, computed: { ...mapGetters(['resturantName']) // 动态计算属性,相当于this.$store.getters.resturantName } } ##script## <span class="hljs-comment"><!-- Add "scoped" attribute to limit CSS to this component only --></span> <span class="hljs-tag"><<span class="hljs-name">style</span> <span class="hljs-attr">scoped</span>></span><span class="css"> <span class="hljs-selector-class">.title</span>,<span class="hljs-selector-class">.titleName</span>{ <span class="hljs-attribute">color</span>: red; <span class="hljs-attribute">font-size</span>: <span class="hljs-number">20px</span>; } <span class="hljs-selector-class">.btn</span>{ <span class="hljs-attribute">width</span>: <span class="hljs-number">160px</span>; <span class="hljs-attribute">height</span>: <span class="hljs-number">40px</span>; <span class="hljs-attribute">background-color</span>: red; <span class="hljs-attribute">border</span>: none; <span class="hljs-attribute">outline</span>: none; <span class="hljs-attribute">color</span>: <span class="hljs-number">#ffffff</span>; <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>; } <span class="hljs-selector-class">.marTop</span>{ <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">20px</span>; } </span><span class="hljs-tag"></<span class="hljs-name">style</span>></span> |