em 和rem的区别:rem是根据html根节点来计算的,而em是继承父元素的字体;一句话概括:em相对于父元素,rem相对于根元素。
第二:项目中遇到过什么难题(最深刻的体会)
第三:vue组件之间通讯
方法一:props/$emit
父组件 A 通过 props 的方式向子组件 B 传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。
1. 父组件向子组件传值
接下来我们通过一个例子,说明父组件如何向子组件传递值:在子组件 Users.vue 中如何获取父组件 App.vue 中的数据 users:["Henry","Bucky","Emily"]
//App.vue父组件
<template>
<div id="app">
<users v-bind:users="users"></users>//前者自定义名称便于子组件调用,后者要传递数据名
</div>
</template>
<script>
import Users from "./components/Users"
export default {
name: 'App',
data(){
return{
users:["Henry","Bucky","Emily"]
}
},
components:{
"users":Users
}
}
//users子组件
<template>
<div class="hello">
<ul>
<li v-for="user in users">{{user}}</li>//遍历传递过来的值,然后呈现到页面
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props:{
users:{ //这个就是父组件中子标签自定义名字
type:Array,
required:true
}
}
}
</script>
总结:父组件通过 props 向下传递数据给子组件。注:组件中的数据共有三种形式:data、props、computed
2. 子组件向父组件传值(通过事件形式)
接下来我们通过一个例子,说明子组件如何向父组件传递值:当我们点击“Vue.js Demo”后,子组件向父组件传递值,文字由原来的“传递的是一个值”变成“子向父组件传值”,实现子组件向父组件值的传递。
// 子组件
<template>
<header>
<h1 @click="changeTitle">{{title}}</h1>//绑定一个点击事件
</header>
</template>
<script>
export default {
name: 'app-header',
data() {
return {
title:"Vue.js Demo"
}
},
methods:{
changeTitle() {
this.$emit("titleChanged","子向父组件传值");//自定义事件 传递值“子向父组件传值”
}
}
}
</script>
// 父组件
<template>
<div id="app">
<app-header v-on:titleChanged="updateTitle" ></app-header>//与子组件titleChanged自定义事件保持一致
// updateTitle($event)接受传递过来的文字
<h2>{{title}}</h2>
</div>
</template>
<script>
import Header from "./components/Header"
export default {
name: 'App',
data(){
return{
title:"传递的是一个值"
}
},
methods:{
updateTitle(e){ //声明这个函数
this.title = e;
}
},
components:{
"app-header":Header,
}
}
</script>
总结:子组件通过 events 给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。
方法二、$emit/$on
这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案 vuex。
1.具体实现方式:
var Event=new Vue();
Event.$emit(事件名,数据);
Event.$on(事件名,data => {});
2.举个例子
假设兄弟组件有三个,分别是 A、B、C 组件,C 组件如何获取 A 或者 B 组件的数据
<div id="itany">
<my-a></my-a>
<my-b></my-b>
<my-c></my-c>
</div>
<template id="a">
<div>
<h3>A组件:{{name}}</h3>
<button @click="send">将数据发送给C组件</button>
</div>
</template>
<template id="b">
<div>
<h3>B组件:{{age}}</h3>
<button @click="send">将数组发送给C组件</button>
</div>
</template>
<template id="c">
<div>
<h3>C组件:{{name}},{{age}}</h3>
</div>
</template>
<script>
var Event = new Vue();//定义一个空的Vue实例
var A = {
template: '#a',
data() {
return {
name: 'tom'
}
},
methods: {
send() {
Event.$emit('data-a', this.name);
}
}
}
var B = {
template: '#b',
data() {
return {
age: 20
}
},
methods: {
send() {
Event.$emit('data-b', this.age);
}
}
}
var C = {
template: '#c',
data() {
return {
name: '',
age: ""
}
},
mounted() {//在模板编译完成后执行
Event.$on('data-a',name => {
this.name = name;//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
})
Event.$on('data-b',age => {
this.age = age;
})
}
}
var vm = new Vue({
el: '#itany',
components: {
'my-a': A,
'my-b': B,
'my-c': C
}
});
</script>
$on 监听了自定义事件 data-a 和 data-b,因为有时不确定何时会触发事件,一般会在 mounted 或 created 钩子中来监听。
方法三、vuex
1. 简要介绍 Vuex 原理
Vuex 实现了一个单向数据流,在全局拥有一个 State 存放数据,当组件要更改 State 中的数据时,必须通过 Mutation 进行,Mutation 同时提供了订阅者模式供外部插件调用获取 State 数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走 Action,但 Action 也是无法直接修改 State 的,还是需要通过 Mutation 来修改 State 的数据。最后,根据 State 的变化,渲染到视图上。
2. 简要介绍各模块在流程中的功能:
Vue Components:Vue 组件。HTML 页面上,负责接收用户操作等交互行为,执行 dispatch 方法触发对应 action 进行回应。
dispatch:操作行为触发方法,是唯一能执行 action 的方法。
actions:操作行为处理模块,由组件中的$store.dispatch('action 名称', data1)来触发。然后由 commit()来触发 mutation 的调用 , 间接更新 state。负责处理 Vue Components 接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台 API 请求的操作就在这个模块中进行,包括触发其他 action 以及提交 mutation 的操作。该模块提供了 Promise 的封装,以支持 action 的链式触发。
commit:状态改变提交操作方法。对 mutation 进行提交,是唯一能执行 mutation 的方法。
mutations:状态改变操作方法,由 actions 中的commit('mutation 名称')来触发。是 Vuex 修改 state 的唯一推荐方法。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些 hook 暴露出来,以进行 state 的监控等。
state:页面状态管理容器对象。集中存储 Vue components 中 data 对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用 Vue 的细粒度数据响应机制来进行高效的状态更新。
getters:state 对象读取方法。图中没有单独列出该模块,应该被包含在了 render 中,Vue Components 通过该方法读取全局 state 对象。
3. Vuex 与 localStorage
vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在 vuex 里数据改变的时候把数据拷贝一份保存到 localStorage 里面,刷新之后,如果 localStorage 里有保存的数据,取出来再替换 store 里的 state。
let defaultCity = "上海"
try { // 用户关闭了本地存储功能,此时在外层加个try...catch
if (!defaultCity){
defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
}
}catch(e){}
export default new Vuex.Store({
state: {
city: defaultCity
},
mutations: {
changeCity(state, city) {
state.city = city
try {
window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
// 数据改变的时候把数据拷贝一份保存到localStorage里面
} catch (e) {}
}
}
})
这里需要注意的是:由于 vuex 里,我们保存的状态,都是数组,而 localStorage 只支持字符串,所以需要用 JSON 转换:
JSON.stringify(state.subscribeList); // array -> string
JSON.parse(window.localStorage.getItem("subscribeList")); // string -> array
方法四、$attrs/$listeners
1. 简介
多级组件嵌套需要传递数据时,通常使用的方法是通过 vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此 Vue2.4 版本提供了另一种方法----$attrs/$listeners
$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。
$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
接下来我们看个跨级通信的例子:
// index.vue
<template>
<div>
<h2>浪里行舟</h2>
<child-com1
:foo="foo"
:boo="boo"
:coo="coo"
:doo="doo"
title="前端工匠"
></child-com1>
</div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
components: { childCom1 },
data() {
return {
foo: "Javascript",
boo: "Html",
coo: "CSS",
doo: "Vue"
};
}
};
</script>
// childCom1.vue
<template class="border">
<div>
<p>foo: {{ foo }}</p>
<p>childCom1的$attrs: {{ $attrs }}</p>
<child-com2 v-bind="$attrs"></child-com2>
</div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
components: {
childCom2
},
inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
props: {
foo: String // foo作为props属性绑定
},
created() {
console.log(this.$attrs); // { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端工匠" }
}
};
</script>
// childCom2.vue
<template>
<div class="border">
<p>boo: {{ boo }}</p>
<p>childCom2: {{ $attrs }}</p>
<child-com3 v-bind="$attrs"></child-com3>
</div>
</template>
<script>
const childCom3 = () => import("./childCom3.vue");
export default {
components: {
childCom3
},
inheritAttrs: false,
props: {
boo: String
},
created() {
console.log(this.$attrs); // { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端工匠" }
}
};
</script>
// childCom3.vue
<template>
<div class="border">
<p>childCom3: {{ $attrs }}</p>
</div>
</template>
<script>
export default {
props: {
coo: String,
title: String
}
};
</script>
网友评论