# Vue 组件通信
-
父传子 prop
-
父传子 插槽 slot
-
子传父 $emit
-
子传父,定义 ref
-
vuex
-
父传子,.sync
-
$children $parent 尽量别用,用也是取,尽量别改
-
案例 1
-
parent1.vue
<template> <div> <p>父组件的data:{{ count }}</p> <button @click="getSonNum">获取子组件的 number</button> <Son1 /> </div> </template> <script> import Son1 from "./Son1"; export default { name: "Parent1", components: { Son1 }, data() { return { count: 2000, }; }, methods: { change(newValue) { this.count = newValue; }, getSonNum() { console.log(this.$children[0].number); }, }, }; </script> -
son1.vue
<template> <div> <p>我是子组件1,使用父组件的data {{ count }}</p> <button @click="handleClick"> 通过$parent调用父组件的事件修改父组件的data </button> </div> </template> <script> export default { name: "Son1", data() { return { number: 5, }; }, computed: { count() { // console.log(this.$parent.$data.count); // $data可以省略 // console.log(this.$parent.count); return this.$parent.count; }, }, methods: { handleClick() { this.$parent.change(200); }, }, }; </script>
-
-
-
依赖注入 provide 和 inject
-
Parent2.vue
<template> <div> <p>我是父组件2中的 data:{{ count }}</p> <Son2 str="hello" class="active" title="xxx" id="fsfs" @click=" () => { console.log('hello'); } " /> </div> </template> <script> import Son2 from "./Son2"; export default { name: "Parent2", components: { Son2 }, data() { return { count: 100, }; }, provide() { return { // 向下传递的时候,要写成函数带返回值的形式,这样后代才能动态获取 // 只能写成箭头函数,不能写成普通函数,因为普通函数的this指向的是 return{} count: () => this.count, changeCount: this.changeCount, }; }, methods: { changeCount(newCount) { this.count = newCount; }, }, }; </script> -
Son2.vue
<template> <div> <p>子组件2使用父组件2中的 data:{{ count() }}</p> <p>父组件传递的str props:{{ $attrs.str }}</p> <GrandSon2 /> </div> </template> <script> import GrandSon2 from "./GrandSon2"; export default { name: "Son2", components: { GrandSon2 }, inject: ["count"], created() { // 获取当前组件的所有 html 属性,就是父组件使用我的时候标签内传递的所有属性,class除外 console.log(this.$attrs); // 获取传递的事件 console.log(this.$listeners); }, }; </script> -
GrandSon2.vue
<template> <div> <p>子子组件2使用父父组件2中的 data:{{ count() }}</p> <button @click="handleClick">使用inject获取祖先的修改事件并执行</button> </div> </template> <script> export default { name: "Parent2", inject: ["count", "changeCount"], methods: { handleClick() { this.changeCount(5000); // 如果provide只是传值的话只是修改了 Parent2,剩下的两个组件没有被修改 // 把 provide 的 count 改成了函数,就会全部跟着修改 }, }, }; </script>
-
# 渲染函数 & JSX
灵活性比较强,想 是什么标签就是什么标签的时候用渲染函数或 jsx
-
渲染函数 render(https://cn.vuejs.org/v2/guide/render-function.html)-
RenderDemo.vue
<template> <div> <!-- 需求:想创建一个组件,等级标题组件 --> <LevelTitle :level="1">标题1</LevelTitle> <LevelTitle :level="2">标题2</LevelTitle> <LevelTitle :level="3">标题3</LevelTitle> </div> </template> <script> import LevelTitle from "./LevelTitle"; export default { name: "RenderDemo", components: { LevelTitle }, }; </script> -
LevelTitle.vue
<!-- 这里不能写template标签,因为下面使用了createElement --> <script> export default { name: "LevelTitle", props: ["level"], // 组件内的 render 函数,可以帮我们创建 template // 创建 template 需要使用 createElement 方法(是render 函数自带的参数) // createElement 的使用 // 参数1 字符串(标签名)/对象/函数 必选 // 参数2 对象(一个与模板中attribute对应的数据对象) 非必选 // 该对象内可以设置 class style 事件绑定 prop …… // 参数3 字符串/数组 表示的都是标签的内容,注意标签添加子标签需要使用 createElement 非必选 render(createElement) { return createElement("h" + this.level, this.$slots.default); /* return createElement( "h" + this.level, { class: "test", on: { click() { console.log(111); }, }, }, // [createElement("span", "哈哈哈哈"), "hello"] [ this.$slots.default, this.level === 1 ? createElement("span", "我是标题1") : "", ] ); */ }, }; </script>
-
-
JSX
使用 jsx 简化 render 函数
jsx 语法支持在 js 中写 html
如果 jsx 语法中 在 html 内想要 js 的话 使用 {} 包裹-
RenderDemo.vue
<template> <div> <!-- 需求:想创建一个组件,等级标题组件 --> <LevelTitle :level="1">标题1</LevelTitle> <LevelTitle :level="2">标题2</LevelTitle> <LevelTitle :level="3">标题3</LevelTitle> </div> </template> <script> import LevelTitle from "./LevelTitle"; export default { name: "RenderDemo", components: { LevelTitle }, }; </script> -
LevelTitle.vue
<script> export default { name: "LevelTitle", props: ["level"], render() { const tag = "h" + this.level; return ( <tag onclick={this.x}> {this.$slots.default} {this.level === 1 ? <span>我是1</span> : ""} </tag> ); }, methods: { x() { console.log("x"); }, }, }; </script>
-
# 函数式组件
函数组件和普通组件没区别
如果 一个组件只接收 props 没有自己的 data ,没有 data 的组件可以称为无状态组件, 此时可以将这个组件设置成函数组件
两种组件使用和创建都一样,只不过 函数组件渲染会快
-
单文件形式的组件
-
RenderDemo.vue
<template> <div> <h3>函数组件</h3> <FunComponent :list="list" :handleClick="handleClick"> <h4>测试 slot</h4> </FunComponent> </div> </template> <script> import FunComponent from "./FunComponent"; export default { name: "RenderDemo", components: { FunComponent, }, data() { return { list: [1, 2, 3], }; }, methods: { handleClick() { console.log("传递事件"); }, }, }; </script> -
FunComponent.vue
<template functional> <div> <!-- 函数组件一般不需要设置 script 标签 --> <!-- 接收的 props 直接在 template 使用 props.xx 接收并使用 --> <ul> <li @click="props.handleClick" v-for="item in props.list" :key="item"> {{ item }} </li> </ul> <slot /> </div> </template>
-
-
js 形式的组件
-
FunComponent1.js
// 使用 js 文件创建组件 // 局部组件 // 普通的局部组件就是一个对象,对象内必须添加render方法,返回 vnode 节点 /* export default { name: "FunComponent1", props: ["title"], render() { return ( <div style="color:red"> 局部函数组件{this.title} {this.$slots.default} </div> ); }, created() { console.log(1111); }, }; */ // js 文件的函数式组件创建 // 当 functional 属性为 true 该组件就是函数组件,就不能使用 this // 想要获取组件的一切,需要使用 render 方法的第二个参数 context. // 详情参考文档 // https://cn.vuejs.org/v2/guide/render-function.html#%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BB%84%E4%BB%B6 /* export default { name: "FunComponent1", functional: true, render(h, context) { // context 上下文,组件的一切都是通过 context 去拿 return ( <div style="color:red"> 局部函数组件{context.props.title} {context.children} // {context.slots().default} // 这样写也可以 </div> ); }, }; */ import Vue from "vue"; // 全局组件 Vue.component("FunComponent1", { props: ["title"], render() { return <div>全局组件{this.title}</div>; }, }); -
RenderDemo.vue
<template> <div> <FunComponent1 title="hello" class="x" id="x"> <span>world</span> </FunComponent1> </div> </template> <script> // import FunComponent1 from "./FunComponent1"; // 注释了也能运行是因为 FunComponent1 全局注册了 export default { name: "RenderDemo", components: { // FunComponent1, }, }; </script> -
main.js
import "./components/FunComponent1";
-
# 插件
Vue.use() 主要就是执行该对象里边的 install 方法
在 vue 中为 vue 添加全局功能
全局 property,过滤器,prototype ……
-
main.js
import "./plugins/index"; // 可以当作很多 js 的全局变量 Vue.xx = "100"; // 在所有 vue 组件中创建了一个全局变量 Vue.prototype.$xx = 100; -
PluginDemo.vue
<template> <div> <!-- 有些时候使用 Vue.use 方法就给 vue 添加了一些全局功能 --> <input type="text" v-focus="true" /> <!-- 这里的true就是binding.value --> </div> </template> <script> import Vue from "vue"; export default { name: "PluginDemo", created() { console.log(this.$xx); console.log(Vue.xx); console.log(this.$num); }, }; </script> -
plugins 文件夹下 index.js 文件
import Vue from "vue"; // 制作一个插件 const myPlugin = { install() { // 组件内全局的变量 Vue.prototype.$num = 1000; // 组件的全局指令 v-focus Vue.directive("focus", { inserted: function (el, binding) { // binding是一个对象,binding.value代表所绑定的值 binding.value && el.focus(); }, }); }, }; // use方法主要就是执行了 参数内的 install 方法 // install 就相当于是为 use 专门而生的 Vue.use(myPlugin); // export default myPlugin;
# 混入
用的很少,但可能会用到。全局混入禁止使用
mixin 混入之后, 会自动和组件内的内容进行合并 ,有的合并有的会覆盖,组件本身优先级高,混入的优先执行
-
MixinDemo.vue
<template> <div>111</div> </template> <script> import myMixin from "../mixins/index"; export default { name: "MixinDemo", // 主要是看混入生命周期 mixins: [myMixin], data() { return { num: 10, }; }, created() { console.log("组件自己的created"); console.log(this.num); //10 }, }; </script> -
mixins 文件夹下的 index.js
// 设置混入对象 就是组件内的一部分内容 const myMixin = { created() { // this.hello(); console.log("我是 minxin 内的声明周期 created"); }, data() { return { num: 100, }; }, /* methods: { hello: function() { console.log("hello from mixin!"); }, }, */ }; export default myMixin;
# 格式化文件
在根目录创建一个.prettierrc 文件
{
"semi": false, //末尾是否要分号
"singleQuote": true //用单引号代替双引号
}
# 跨域请求数据
-
index.js
// 利用 node + express(依赖) 搭建简易服务器 const express = require("express"); // 利用 express 创建一个 app const app = express(); // 设置一个端口 3000 const port = 3000; // 只要是地址的协议,域名,端口号三者有一个不同就是跨域。 //设置跨域访问 app.all("*", (req, res, next) => { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "X-Requested-With"); res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS"); res.header("Content-Type", "application/json;charset=utf-8"); next(); }); // 后台反馈的权限菜单数组 const authMenu = [ { pid: -1, name: "购物车", id: 1, auth: "cart" }, { pid: 1, name: "购物车列表", id: 4, auth: "cart-list" }, { pid: 4, name: "彩票", id: 5, auth: "lottery" }, { pid: 4, name: "商品", id: 6, auth: "product" }, { pid: -1, name: "商店", id: 2, auth: "shop" }, { pid: -1, name: "个人中心", id: 3, auth: "store" }, ]; // 设置一个路由,相当于定义一个接口,返回对应的数据 // get 方法,地址是 localhost:3000,返回值是"Hello World!" app.get("/", (req, res) => { res.send(authMenu); }); // post 方法,要设置接收参数的话,需要借助一个 /* app.get("/authmenu", (req, res) => { res.send(authMenu); }); */ // 在对应的端口启动服务器 app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`); }); // 用 node index.js 执行 -
getTree.js
// 制作一个处理扁平化数组的函数,将其转换成树 const getTree = (authMenus) => { const menu = []; const sourceMap = {}; authMenus.forEach((m) => { m.children = []; sourceMap[m.id] = m; if (m.pid === -1) { // 根目录 menu.push(m); } else { sourceMap[m.pid].children.push(m); } }); return menu; }; export { getTree };
# 权限菜单
$router.addRoute (‘父路由 name’, 要添加的对象) 添加一条新的路由规则作为现有路由的子路由
如果在组件中使用 路由实例的 addRoute 方法,动态添加路由不会生效,需要到路由导航守卫中添加
-
router 文件夹下 index.js
import Vue from "vue"; import VueRouter from "vue-router"; Vue.use(VueRouter); // 因为权限问题,该项目的页面用户看到的是不同的,如何实现 // 1.我们可以将所有页面全部写好,然后在需要权限的页面添加判断() // 2.根据权限动态制作路由,添加到vue的路由中 // 提前写好所有的路由数组,根据权限筛选,然后添加到项目中 // 方案二的实现 // 创建一个完整的权限路由数组,加上完整的页面。供以后筛选使用 export const authRoutes = [ { name: "cart", path: "cart", component: () => import("../views/Cart"), children: [ { name: "cartlist", path: "cartlist", component: () => import("../views/CartList"), children: [ { name: "lottery", path: "lottery", component: () => import("../views/Lottery.vue"), }, { name: "product", path: "product", component: () => import("../views/Product.vue"), }, ], }, ], }, { name: "shop", path: "shop", component: () => import("../views/Shop.vue"), }, { name: "store", path: "store", component: () => import("../views/Store.vue"), }, ]; export default router;
# 如何生成打包报告
打包时,为了直观地发现项目中存在的问题,可以在打包时生成报告。
-
vue-cli-service build --report
-
VueUI 中 - 控制台 - 分析面板