Vue项目构建
1. 安装启动
- 安装包:
npm i -g @vue/cli
- 创建项目:
vue create xxxx
- 启动项目:切换到项目目录后,运行
npm run serve
2. 项目目录
|-- public
|-- favicon.ico:页签图标
|-- index.html:主页面
|-- src
|-- App.vue:汇总所有组件
|-- main.js:入口文件
|-- assets:存放静态资源
|-- logo.png
|-- components:存放组件
|-- HelloWorld.vue
|-- .gitignore:git版本控制忽略的配置
|-- babel.config.js:babel的配置文件
|-- package-lock.json:包版本控制文件
|-- package.json:应用包配置文件
|-- README.md:应用描述文件
3. public/index.html 文件相关说明
<head>
<meta charset="utf-8">
<!-- 针对IE浏览器的配置:让IE浏览器以最高的渲染级别渲染页面 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 开启移动端的理想视口 -->
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- 配置页签图标 <%= BASE_URL %> 指的是public文件夹的路径 -->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!-- 配置网页标题:指的是package.json中配置的name属性 -->
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
</head>
<body>
<!-- 当浏览器不支持js时,noscript标签中的元素就会被渲染 -->
<noscript>
<strong>文字</strong>
</noscript>
<!-- 容器 -->
<div id="app"></div>
</body>
4. 单文件组件的写法
<template>
<div class="demo">
<h2>姓名:{{ studentName }}</h2>
<h2>年龄:{{ age }}</h2>
<button @click="showName">点我显示姓名</button>
</div>
</template>
<script>
export default {
name: "School",
data() {
return {
studentName: "LMY",
age: 22,
};
},
methods: {
showName() {
alert(this.studentName);
},
},
};
</script>
<style>
.demo {
color: tomato;
}
</style>
5. src/main.js 文件相关说明
// 该文件是整个项目的入口文件
import Vue from "vue";
// 引入App组件,该组件是其他所有组件的父组件
import App from "./App.vue";
Vue.config.productionTip = false;
new Vue({
// 将app组件放入容器中
render: (h) => h(App),
// render: (h) => h("h1", "你好啊"),
}).$mount("#app");
/*
当前引入的Vue是vue.runtime.xxx.js,只包含核心功能,没有模板解析器
所以无法在new Vue的时候指定template配置项来指定页面内容
需要使用render函数接收到的createElement即h去指定具体内容
*/
6. vue.config.js
该文件可以对脚手架的webpack配置进行一些修改,文件中的配置项会覆盖默认配置
module.exports = {
// 配置项不能为空,覆盖后会导致配置错误,项目无法启动
pages: {
index: {
// 重新指定入口
entry: "src/main.js",
},
},
// 关闭语法检查
lintOnSave: false,
};
详情参考 https://cli.vuejs.org/zh/config/#vue-config-js
ref 标签属性
被用来给元素或子组件注册引用信息
<!-- 注册引用 -->
<h1 v-text="msg" ref="title"></h1>
<school ref="sch"/>
// 给标签加ref属性,获取到DOM元素
console.log("@", this.$refs.title);
// 给组件加ref属性,获取到组件实例对象vc
console.log("@", this.$refs.sch);
props配置项
作用:让组件接收外部传进来的数据
接收数据
<student name="feidu" :age="22" gender="男"/>
传递数据
// 简单接收
props: ["name", "gender", "age"],
// 限制类型
props: {
name: String,
age: Number,
gender: String,
},
// 其他限制
props: {
name: {
type: String,
required: true, // 必须传
},
age: {
type: Number,
default: 18, // 指定默认值
},
gender: {
type: String,
required: true,
},
},
// 在模板中可以直接使用,和data用法一致
注意:props是只读的,Vue底层会监测你对props的修改,如果进行了修改就会报错。想要修改可以将prop的值复制到data中然后修改data中的数据
mixins配置项
作用:可以把多个组件共用的配置提取成一个混合对象
定义混合 mixin.js
export const mixin = {
methods: {
showName() {
alert(this.name);
},
},
};
局部使用混合
import { mixin } from "../mixin";
// 配置项
mixins: [mixin],
全局使用混合:在main.js中修改
import { mixin } from "../mixin";
Vue.mixin(mixin);
注意:
- 如果混合中配置项是methods和data,那么会将原有的和混合中的放在一起使用,如果存在冲突,以组件中原来定义的为准
- 如果像生命周期钩子函数这种配置,会将函数中的代码进行合并,此时混合中的代码在前
插件
- 作用:用于增强Vue
- 本质:包含install方法的一个对象,install 的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
定义插件 plugins.js
export default {
install(Vue, x) {
console.log("@install", Vue, x);
// 可以定义全局过滤器、全局指令、混合
// 添加全局方法
Vue.prototype.hello = () => { alert("hello"); }
},
};
使用插件:在main.js中修改
// 引入插件
import plugins from "./plugins";
// 应用插件:应用插件后install方法就会调用进行一些增强的操作
Vue.use(plugins, 1);
scoped样式
在组件中定义样式
<style scoped>
.demo {
background-color: rosybrown;
}
</style>
此时样式只在当前组件中生效
WebStorage
- 存储内容大小一般支持5MB左右(不同的浏览器可能不同)
- 浏览器端通过
window.localStorage
和window.sessionStorage
属性来实现本地存储机制
相关API
<div id="root">
<h2>LocalStorage</h2>
<button onclick="saveData()">点我保存数据</button>
<button onclick="readData()">点我读取数据</button>
<button onclick="deleteData()">点我删除数据</button>
<button onclick="deleteAllData()">点我清空数据</button>
</div>
<script>
// F12 在Application选项卡中查看
function saveData() {
localStorage.setItem("msg", "dudu");
// 两个参数都是字符串类型,如果数据不是字符串可以使用JSON来进行转换
localStorage.setItem("book", JSON.stringify({ name: 'modu', author: "Priest" }));
}
function readData() {
console.log(localStorage.getItem("msg"));
// 如果读取的值不存在,返回值为null,JSON.parse(null)的结果还是null
console.log(JSON.parse(localStorage.getItem("book")));
}
function deleteData() {
localStorage.removeItem("msg");
}
function deleteAllData() {
localStorage.clear();
}
// localStorage中的数据在浏览器关闭后也不会消失,需要手动调用API或者清除浏览器缓存
// sessionStorage 与 localStorage使用的API完全相同,区别在于浏览器关闭后sessionStorage中的数据就会消失
</script>
注意:sessionStorage 与 localStorage使用的API完全相同,区别在于浏览器关闭后sessionStorage中的数据就会消失,而localStorage中的数据需要手动调用API或者清除浏览器缓存才可以删除
组件自定义事件
子组件给父组件传递数据方式一:通过父组件给子组件传递函数类型的props来实现
父组件 App.vue
<template>
<div class="app">
<school :getSchoolName="getSchoolName" />
</div>
</template>
<script>
export default {
methods: {
getSchoolName(name) {
console.log(`APP组件收到了学校名:${name}`);
},
}
</script>
子组件 School.vue
<template>
<div class="school">
<h2>学校名称:{{ name }}</h2>
<button @click="sendSchoolName">点我发送学校名</button>
</div>
</template>
<script>
export default {
props: ["getSchoolName"],
methods: {
sendSchoolName() {
this.getSchoolName(this.name);
},
},
</script>
子组件给父组件传递数据方式二:通过父组件给子组件绑定一个自定义事件实现
父组件 App.vue
<template>
<div class="app">
<!-- 绑定一个名字为dudu的自定义事件 -->
<student @dudu="getStudentName" />
</div>
</template>
<script>
export default {
methods: {
// 定义事件触发后的回调函数,可以使用ES6中的 ...params 来接收参数
getStudentName(name) {
console.log(`APP组件收到了学生名:${name}`);
},
}
</script>
子组件 Student.vue
<template>
<div class="student">
<h2>姓名:{{ name }}</h2>
<button @click="sendStudentName">点我发送学生名</button>
</div>
</template>
<script>
export default {
methods: {
sendStudentName() {
// 触发自定义事件:第一个参数为事件名,后面的参数可以有很多,用来传递需要的数据
this.$emit("dudu", this.name);
},
},
</script>
在父组件中也可以在JS代码中绑定事件
<template>
<div class="app">
<student ref="student" />
</div>
</template>
<script>
export default {
mounted() {
// 绑定自定义事件的另一种方式
this.$refs.student.$on("dudu", this.getStudentName);
},
methods: {
getStudentName(name) {
console.log(`APP组件收到了学生名:${name}`);
},
}
</script>
如果事件只触发一次那么两种绑定方式分别为:
<student @dudu.once="getStudentName" />
this.$refs.student.$once("dudu", this.getStudentName);
解除绑定自定义事件
// 解绑一个自定义事件
this.$off("dudu");
// 解绑多个自定义事件
this.$off(["dudu", "demo"]);
// 解绑所有的自定义事件
this.$off();
注意:组件实例被销毁(可以调用
this.$destroy()
或者路由的方式)后,组件实例上的所有自定义事件都会自动解绑
一些注意事项
在JS代码中绑定事件时,可以将第二个参数直接写回调函数的内容,但回调函数一定要使用箭头函数的形式
this.$refs.student.$on("dudu", (name) => {
console.log(`APP组件收到了学生名:${name}`);
console.log(this);
});
因为事件回调函数中的this默认是触发事件的对象,也就是Student组件实例对象,此时就无法使用this来操作App组件实例对象中的数据
this.$refs.student.$on("dudu", function (name) {
console.log(`APP组件收到了学生名:${name}`);
console.log(this);
});
不可以使用@click="show"
直接给组件绑定原生DOM事件,Vue会将该click事件当做自定义事件处理,如果要绑定,需要使用@click.native="show"
全局事件总线
- 一种组件间通信的方式,适用于任意组件间通信
安装全局事件总线:main.js
new Vue({
render: (h) => h(App),
// 安装全局事件总线
beforeCreate() {
// 将当前应用的vm保存到原型对象中,使之后的每个组件都可以操作它
Vue.prototype.$bus = this;
},
}).$mount("#app");
使用全局事件总线:
// 在组件上绑定事件
mounted() {
this.$bus.$on("dudu", (data) => {
console.log("我是School组件", data);
});
},
// 在组件销毁之前解绑自己在总线上绑定的事件
beforeDestroy() {
this.$bus.$off("dudu");
},
// 在另一个组件上触发该事件,并传递数据
methods: {
sendStudentName() {
this.$bus.$emit("dudu", 666);
},
},
消息订阅与发布
- 一种组件间通信的方式,适用于任意组件间通信
- 一种实现:pubsub-js
安装: npm i pubsub-js
引入:import pubsub from "pubsub-js";
使用:
mounted() {
// 订阅消息:msgName是事件名,data是参数
this.pubId = pubsub.subscribe("dudu", (msgName, data) => {
console.log("有人发布了dudu消息,dudu消息的回调执行了...", msgName, data);
console.log(this); // this的规则和自定义事件相同
});
},
beforeDestroy() {
// 取消订阅:类似定时器的取消方式
pubsub.unsubscribe(this.pubId);
},
sendStudentName() {
pubsub.publish("dudu", this.name);
},
$nextTick
- 语法:
this.$nextTick(回调函数)
- 作用:在下一次DOM更新结束后执行其指定的回调
- 用法:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行