npm全局安装
切换淘宝npm镜像
npm config set registry https://registry.npm.taobao.org
全局安装
npm install -g @vue/cli
切换到要创建的目录
vue create xxx
启动项目
npm run serve
vue脚手架结构
加入School和Student组件到components目录下
修改App.vue
修改main.js。注册app组件,加入template
运行npm run serve
render函数
vue.js与vue.runtime.xxx.js的区别
(1)vue.js是完整版的vue,包含:核心功能+模板解析器
(2)vue.runtime.xxx.js是运行版的vue,只包含:核心功能,没有模板解析器
没有模板解析器不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
/*
render(createElement){
return createElement('h2','helloworld');
}
*/
render: h => h(App),
}).$mount('#app')
index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
</body>
</html>
修改默认配置
vue脚手架隐藏了所有webpack配置,使用
vue inspect > output.js
使用vue.config.js对脚手架进行个性化定制,配置参考 | Vue CLI (vuejs.org)
module.exports = {
pages: {
index: {
// page 的入口
entry: 'src/main.js',
},
},
lintOnSave:false
}
ref属性
用来给元素或子组件注册引用信息(id的替代者)
应用在html标签上获取的是真实Dom元素,应用在组件标签上是组件实例对象(vc)
使用方式 标识 <h1 ref="xxx"></h1> 或 <School ref="xxx"></School> 获取:this.$refs.xxx
<Student ref="Stu"></Student>
<button @click="showDom" ref="btn">点击打印元素或组件</button>
methods:{
showDom(){
console.log(this.$refs.Stu) //组件实例对象
console.log(this.$refs.btn) //真实dom元素
}
}
props配置
让组件接收外部传过来的数据
(1)传递数据
<School :no="211" name="武汉纺织大学"></School>
(2)接收数据
//(1)只接收
props:['no','name','address']
//(2)限制类型
props:{
no:Number,
name:String,
address:String
}
//(3)限制类型,必要性,默认值
props:{
no:{
type:Number,
required:false,
default:999
},
name:{
type:String,
required: true
},
address:{
type:String,
required:false,
default: '未知'
}
}
⚠️props是只读的,vue底层会监测你对props的修改,如果进行修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到新的变量在data中,去进行修改
Shcool.vue
<template>
<div>
<h2>编号:{{ CurrentNo }}</h2>
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
<button @click="CurrentNo++">点我编号+1</button>
</div>
</template>
<script>
export default {
name: "School",
data() {
return {
CurrentNo : this.no
}
},
// props:['no','name','address']
/*props:{
no:Number,
name:String,
address:String
},*/
props:{
no:{
type:Number,
required:false,
default:999
},
name:{
type:String,
required: true
},
address:{
type:String,
required:false,
default: '未知'
}
}
}
</script>
<style scoped>
h2 {
color: #0dff1d;
}
</style>
app.vue
<template>
<div>
<School :no="211" name="武汉纺织大学"></School>
</div>
</template>
<script>
import School from './components/School'
export default {
name: "App",
components: {
School
}
}
</script>
minin混入
组件共用的配置提取成一个混入对象
使用方法
(1)定义混合
export const mixin = {
data(){
return{
size: '18cm'
}
}
}
(2)使用混合
import {mixin} from '../mixin';
mixins:[mixin] //局部 -组件
Vue.mixin(mixin) //全局 -main.js
插件
功能:用于增强vue
本质:包含install方法的一个对象,第一个参数是Vue,第二个以后的参数是传递的数据
定义插件
export default {
install(Vue,options) {
Vue.filters()
Vue.directive()
Vue.mixin()
Vue.prototype.$myMethod=function (){}
}
}
使用插件
Vue.use(plugin);
scoped样式
让样式在局部生效,防止冲突
<style scoped></style>
扩展
<style lang="less" scoped>
h2 {
color: #1c036c;
font-weight: 700;
h3{
font-size: 30px;
}
}
</style>
默认vue不识别其他的语法,需要添加加载器
npm view webpack versions #查看webpack版本
npm view less-loader versions #查看less-loader版本
安装less加载器
npm i less-loader@7 #安装7以上版本
npm install less save-dev
TodoList案例关键点
组件命令注意不要和html元素相同
比如:Header
父组件里面的子组件的子组件import的路径
import TodoItem from "@/components/TodoItem";
父组件个子组件传值
<TodoList :todos="todos"/>
<script>
data() {
return {
todos: [
{id: '001', title: '吃饭', done: false},
{id: '002', title: '睡觉', done: true},
{id: '003', title: '打游戏', done: false}
]
}
}
</script>
<TodoItem v-for="item in todos" :key="todos.id" :item="item"/>
<script>
props:["todos"]
</script>
以下修改为$emit 父组件给子组件c
-------
<TodoHeader @addTodo="addTodo"/>
<script>
methods: {
// 增加
addTodo(itemObj) {
this.todos.unshift(itemObj);
}
}
</script>
<script>
<template>
<div class="header">
<input type="text" v-model.trim="addTodoTitle" placeholder="请输入待办事项,按回车键" @keyup.enter="add($event)"/>
</div>
</template>
methods: {
add(event) {
if (this.addTodoTitle != '') {
const itemObj = {id: nanoid(), title: this.addTodoTitle, done: false};
this.$emit('addTodo',itemObj);
}
}
}
</script>
子组件给父组件传值
<TodoHeader :addTodo="addTodo"/>
methods: {
addTodo(itemObj) {
this.todos.unshift(itemObj);
}
}
<input type="text" v-model.trim="addTodoTitle" placeholder="请输入待办事项,按回车键" @keyup.enter="add($event)"/>
<script>
props: ['addTodo'],
methods: {
add(event) {
if (this.addTodoTitle != '') {
const itemObj = {id: nanoid(), title: this.addTodoTitle, done: false};
this.addTodo(itemObj);
}
}
}
</script>
⚠️【data,props,methods,computed】不要有相同的名称!
组件任意传值
beforeCreate() {
Vue.prototype.$bus = this
}
----------------------------------------------------------------
methods: {
// 勾选或取消
changeTodo(id) {
this.todos.forEach((item) => {
if (item.id == id) {
item.done = !item.done;
}
})
}
,
deleteTodo(id) {
this.todos = this.todos.filter((item) => {
return item.id != id;
})
}
}
mounted() {
this.$bus.$on('changeTodo',this.changeTodo);
this.$bus.$on('deleteTodo',this.deleteTodo);
},
beforeDestroy() {
this.$bus.$off('changeTodo');
this.$bus.$off('deleteTodo');
}
----------------------------------------------------------------
methods: {
doneChanged(id) {
this.$bus.$emit('changeTodo',id);
},
delItem(id) {
this.$bus.$emit('deleteTodo',id);
}
},
点击复选框值改变
暂时使用函数,父组件->子组件
🛑不推荐使用v-model:checked=“xxx”,如果xxx是对象里面的值,可以,如果不是,vue不允许。因为vue监视的对象的改变的机制
v-model绑定的值不能是传过来的值,props的值是不能修改的
App.vue
TodoList.vue
TodoItem.vue
App 传递给子组件
<TodoList :todos="todos" :changeTodo="changeTodo"/>
<script>
data() {
return {
todos: [
{id: '001', title: '吃饭', done: false},
{id: '002', title: '睡觉', done: true},
{id: '003', title: '打游戏', done: false}
]
}
},
methods: {
// 勾选或取消
changeTodo(id) {
this.todos.forEach((item) => {
if (item.id == id) {
item.done = !item.done;
}
})
}
}
</script>
TodoList props接收,传递给子组件
<TodoItem v-for="item in todos" :key="todos.id" :item="item" :changeTodo="changeTodo"/>
<script>
props:["changeTodo"]
</script>
TodoItem
<input type="checkbox" v-model:checked="item.done" @change="doneChanged(item.id)"/>
<script>
export default {
name: "TodoItem",
methods: {
doneChanged(id) {
this.changeTodo(id);
}
},
props: ['changeTodo']
}
</script>
组件自定义事件
子组件向父组件传值
$emit实现
通过父组件向子组件绑定一个自定义事件,等子组件触发这个事件,就触发getValue函数
<TodoFooter v-on:customize="getValue"/>
<!--简写-->
<TodoFooter @customize="getValue"/>
<!--只触发一次 <TodoFooter v-on:customize.once="getValue"/> -->
<script>
data() {
return {
context: [
{completed: '', unfinished: ''}
]
}
},
methods: {
getValue(completed, unfinished) {
console.log('已完成数量:' + completed, '未完成数量:' + unfinished)
}
}
</script>
<button @click="fromFather()">from父组件传值</button>
<script>
methods:{
fromFather() {
this.$emit('customize',this.doneTotle, this.todos.length)
}
}
</script>
ref实现
<TodoFooter ref="inputValue"/>
<script>
methods: {
getValue(completed, unfinished) {
console.log('已完成数量:' + completed, '未完成数量:' + unfinished)
},
mounted() {
setTimeout(()=>{
this.$refs.inputValue.$on('customize', this.getValue);
},2000);
//只触发一次
//this.$refs.inputValue.$once('customize', this.getValue);
}
</script>
<button @click="fromFather()">from父组件传值</button>
<script>
methods:{
fromFather() {
this.$emit('customize',this.doneTotle, this.todos.length)
}
}
</script>
解绑自定义事件
//解绑单个自定义事件
this.$off('customize')
//解绑多个自定义事件
this.$off(['xxx','xxx'])
//解绑所有的自定义事件
this.$off()
子组件向父组件传递的值显示在页面上
赋值给data,使用插值语法显示到页面上
<h2>{{context[0].completed}}{{context[0].unfinished}}</h2>
<TodoFooter :todos="todos"
v-on:customize="getValue" ref="inputValue"/>
<script>
data() {
return {
todos: [
{id: '001', title: '吃饭', done: false},
{id: '002', title: '睡觉', done: true},
{id: '003', title: '打游戏', done: false}
],
context: [
{completed: '', unfinished: ''}
]
}
}
,
methods: {
getValue(completed, unfinished) {
console.log('已完成数量:' + completed, '未完成数量:' + unfinished)
this.context[0].completed = completed;
this.context[0].unfinished = unfinished;
}
}
,
mounted() {
setTimeout(() => {
this.$refs.inputValue.$on('customize', this.getValue);
}, 2000)
}
</script>
⚠️注意:回调函数简写时this指向
this.$refs.inputValue.$on('customize', function(completed, unfinished){ this的指向是触发事件的实例对象,谁触发customize就是谁 }); this.$refs.inputValue.$on('customize',(completed, unfinished)=>{ this的指向是外面的组件实例对象 });
绑定原生DOM事件 <Student> @click.native="xxx" </Student>
全局事件总线
组件间通信方式,适用于任意组件间通信
安装
new Vue({
beforeCreate(){
Vue.prototype.$bus = this;
}
})
使用
(1)接收数据:A组件想接收数据,则A组件中的$bus绑定自定义事件,事件的回调留在A组件自身
methods(){
demo(data){}
}
mounted(){
this.$bus.$on('xxx',this.demo)
}
(2)提供数据:this.$bus.$emit('xxx',数据)
最好在
beforeDestroy
钩子中,用$off
解绑当前组件所用到的事件
消息订阅与发布
组件间通信方式,适用于任意组件间通信
使用步骤
1.安装:pubsub npm i pubsub.js
2.引入:import pubsub from 'pubsub.js'
3.接收数据:A组件想接收数据,则A组件中订阅消息,订阅的回调留在A组件本身
methods(){
demo(data){}
mounted(){
this.pid = pubsub.subscribe('xxx',this.demo)//订阅消息,可改为箭头函数,使用funtion(){}匿名函数的this作用域是unfiend
}
}
4.提供数据:pubsub.publish('xxx',数据)
5.最好在beforeDestory钩子中,用pubsub.unsubscribe(pid)取消订阅
$nextTick
将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法
<input v-show="item.isEdit" type="text" v-model="item.title" @blur="alterTitle($event,item)" ref="inputTitle">
editItem(item) {
if (item.hasOwnProperty('isEdit')) {
item.isEdit = true;
}
this.$set(item, 'isEdit', true)
this.$nextTick(function () {
this.$refs.inputTitle.focus();
});
}
过渡与动画
用法
1.准备样式
元素进入的样式
- v-enter:进入的起点
- v-enter-active:进入过程中
- v-enter-to:进入的终点
元素离开的样式
- v-leave:离开的起点
- v-leave-active:离开的过程中
- v-leave-to:离开的终点
2.使用<transition>包裹要过渡的元素,并配置name属性
<transition name="hello">
<h1 v-show="isShow">
你好啊!
</h1>
</transition>
3.若有多个元素需要过渡,则需要使用<transition-group>,且每个元素都要指定key值