目录
一、 分析脚手架
这里来分析Students.Vue组件的模式, 分为template用来书写HTML的地方,script来书写js, style来引入样式,没有可以不写,但是一个组件里面, 必须要包含template和script
School.Vue组件
<template>
<div class="demo">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">点我提示学校名</button>
</div>
</template>
<script>
export default {
name:'School',
data(){
return {
name:'武汉传媒学院',
address:'湖北武汉'
}
},
methods: {
showName(){
alert(this.name)
}
},
}
</script>
<style>
.demo{
background-color: orange;
}
</style>
Students.Vue组件
<template>
<div>
<h2>学生姓名:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
</template>
<script>
export default {
name:'Student',
data(){
return {
name:'张三',
age:18
}
}
}
</script>
App组件:用来引入components文件夹里面的所有小组件
<template>
<div>
<img src="./assets/logo.png" alt="logo">
<School></School>
<Students></Students>
</div>
</template>
<script>
// 引入组件
import School from './components/School'
import Students from './components/Student'
export default {
name:'App',
components: {
School,
Students
}
}
</script>
<!-- <template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
// 引入一个组件
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style> -->
main.js:是整个文件的入口
引入Vue:
import Vue from 'vue'
// 引入App组件 它是所有组件的父组件
// "module": "dist/vue.runtime.esm.js", 引入的是残缺版的Vue
import App from './App.vue'
// 关闭Vue的生产提示
Vue.config.productionTip = false
关于不同版本的Vue:
- vue.js与vue.runtime.xxx.js的区别:
- (1).vue.js是完整版的Vue,包含:核心功能+模板解析器。
- (2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
- 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。
创建Vue实例对象 -- vm
new Vue({
el: '#app',
// 下面这行代码一会解释 完成了这个功能: 将App组件放入容器
// render 渲染
render: h => h(App),
// render(createElement) {
// console.log(typeof createElement) // function 一个函数
// 作用就是创建元素 编写具体的内容
// return createElement('h1', '你好啊')
// }
})
创建Vue实例对象 -- vm 另一种形式
el: '#app',
// 下面这行代码一会解释 完成了这个功能: 将App组件放入容器
render: h => h(App),
}).$mount('#app')
启动组件:ctrl + ~ 可以打开终端,然后配置Vue环境后输入:
npm run revse
运行后 按ctrl键 鼠标就可以点击该链接了!
二、 ref属性
1、什么是 ref?
简单来说,ref
是 Vue 提供的一个 API,用来给元素或者子组件绑定一个引用标识,方便我们在代码里直接拿到这个元素或者组件实例。
在 Vue2 中,使用 $refs
获取; 在 Vue3 的 Composition API 中,使用 ref
或 template ref
获取。
refs 获得组件实例对象
2、为什么要用 ref?
正常来说,Vue 提倡数据驱动视图,不推荐直接操作 DOM,但在实际开发中,有些场景必须要直接拿到元素或组件,比如:
-
需要手动聚焦 input 输入框
-
需要调用子组件的方法
-
需要直接操作第三方库渲染的 DOM
-
需要做一些复杂的 DOM 操作(比如动画控制)
这时候就必须用到 ref
。
总之,当数据驱动搞不定时,ref
就是你的后备方案。
3、怎么用 ref?
3.1 给元素绑定 ref
<template>
<input ref="myInput" />
</template>
<script>
export default {
mounted() {
// 页面加载完,拿到 input 元素,直接调用 focus
this.$refs.myInput.focus();
}
}
</script>
绑定了 ref="myInput"
,通过 this.$refs.myInput
拿到对应 DOM。
3.2 给组件绑定 ref
<template>
<ChildComponent ref="childRef" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
methods: {
callChildMethod() {
this.$refs.childRef.someMethod(); // 调用子组件里的方法
}
}
}
</script>
绑定了 ref="childRef"
,通过 this.$refs.childRef
拿到子组件实例。
3.3 Vue3 Composition API 写法
<template>
<input ref="inputRef" />
</template>
<script setup>
import { ref, onMounted } from 'vue';
const inputRef = ref(null);
onMounted(() => {
inputRef.value.focus();
});
</script>
Vue3 中,ref
是响应式对象,取元素需要 .value
。
注意点(很重要)
-
$refs 只有在 mounted 之后才可以访问到。
-
在 created 阶段访问是 undefined。
-
-
$refs 不是响应式的。
-
它不会触发视图更新,只是单纯的 DOM 引用。
-
-
不要滥用 ref。
-
正常场景用数据驱动优先,实在需要才直接操作 DOM。
-
-
$refs 是只读的,不能直接修改。
-
修改元素属性应该通过标准 DOM 方法或者 Vue 的方式来做。
-
一句话总结
👉 ref就是让你可以在Vue中,直接拿到DOM元素或组件实例的一个快捷通道。
三、props配置
一、什么是 props?
props
是父组件传递数据到子组件的一种机制。
简单理解就是:
父亲给儿子发东西,儿子通过
props
接收。
在 Vue 中,子组件通过 props
来声明要接收哪些数据。
二、为什么要用 props?
因为 Vue 是一个组件化的框架。
组件和组件之间要互相协作,比如:
-
父组件传数据给子组件(比如用户名、商品信息)
-
父组件告诉子组件当前是编辑模式还是查看模式
-
动态地给子组件配置一些不同的初始参数
props 是实现这一切的官方推荐方式。
如果不用 props,每个组件自己拿自己的一份数据,项目会非常混乱,没法维护。
三、怎么用 props?
3.1 父组件传递 props
<template>
<ChildComponent :username="parentName" :age="18" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
data() {
return {
parentName: '小明'
}
}
}
</script>
父组件用 冒号(:属性名="数据"
)绑定数据,传给子组件。
3.2 子组件接收 props
<template>
<div>
<p>姓名:{{ username }}</p>
<p>年龄:{{ age }}</p>
</div>
</template>
<script>
export default {
props: {
username: {
type: String, // 指定类型
required: true // 必填
},
age: {
type: Number,
default: 0 // 默认值
}
}
}
</script>
子组件通过 props
选项声明接收哪些属性、类型是什么、是否必传、默认值是多少。
3.3 只声明字符串数组也可以(简写方式)
export default {
props: ['username', 'age']
}
适合简单场景,复杂的(需要类型校验、默认值等)建议用对象写法。
四、props注意事项
-
props 是单向绑定:父传子,子组件修改自己的 props 会出警告。
-
不要直接修改 props:如果要改,应该用
data
创建一个本地副本。
data() {
return {
localUsername: this.username
}
}
-
可以使用 props 校验,防止父组件传错类型导致出错。
-
动态传递对象/数组要小心,父组件一改,子组件也会响应变化。
-
camelCase / kebab-case 对应问题:
-
js 里是
userName
-
模板里是
user-name
-
一句话总结:
props 是父组件给子组件传递数据的官方途径,规范、安全、高效,但要遵守单向数据流,不要直接改它。
父组件:
在 Vue 中:
-
如果是静态的字符串,可以直接写,不用加
:
。 -
如果是动态的表达式/变量,就必须加
:
。
<Student name="李四" sex="男" :age="18" />
-
name="李四"
:这是静态的字符串,所以直接写就可以了。 -
sex="男"
:也是静态字符串,直接写没问题。 -
:age="18"
:前面加了冒号,因为18
是数字,不是字符串,如果不加:
,Vue会把它当成字符串"18"
,而不是数字18
。
<template>
<div>
<Student name="李四" sex="男" :age="18"/>
</div>
</template>
<script>
import Student from './components/Student'
export default {
name:'App',
components:{Student}
}
</script>
export default {
name:'Student',
data() {
console.log(this)
return {
msg:'我是一个武汉传媒学院的学生',
myAge:this.age
}
},
methods: {
updateAge(){
this.myAge++
}
},
//简单声明接收
// props:['name','age','sex']
//接收的同时对数据进行类型限制
/* props:{
name:String,
age:Number,
sex:String
} */
//接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
props:{
name:{
type:String, //name的类型是字符串
required:true, //name是必要的
},
age:{
type:Number,
default:99 //默认值
},
sex:{
type:String,
required:true
}
}
}
属性 | 意义 |
---|---|
type | 指定这个 prop 应该是什么数据类型(防止传错) |
required | 表示这个 prop 必须传,不传控制台会警告 |
default | 给 prop 指定一个默认值,如果父组件没传就用它 |
小tips
-
required 和 default 不会一起用,一般:
-
必传(required: true)就不用默认值。
-
有默认值(default)就可以不传。
-
-
类型校验只是开发时警告,不会阻止项目运行,所以最好自己传对。
四、mixin混合
什么是 mixin?
mixin 是 Vue 里的混入功能,
简单说:
把多个组件都会用到的代码,提前封装好,统一混进来用。
为什么要用 mixin?
因为有些逻辑重复出现了,
如果每个组件都自己重新写一遍,又麻烦又难维护。
比如:
-
每个组件进来都要打印“页面加载了”
-
每个组件都有一些相同的数据、方法
这时候就应该用 mixin,统一抽取,哪里需要就混入。
怎么用 mixin?
三步走:
① 先定义一个 mixin.js 文件
export const myMixin = {
data() {
return {
message: 'hello from mixin'
}
},
created() {
console.log('mixin的created钩子被调用了')
},
methods: {
greet() {
console.log('mixin的方法greet调用了')
}
}
}
② 在组件中引入使用
<script>
import { myMixin } from './mixin'
export default {
name: 'MyComponent',
mixins: [myMixin], // 👈 核心在这里
created() {
console.log('组件自己的created钩子被调用了')
}
}
</script>
③ 运行效果
-
组件加载的时候,
mixin
里的created()
和 组件自己的created()
都会执行。 -
组件中可以直接用
mixin
里面的data
和methods
,好像就是自己写的一样。
注意⚡️
-
如果 组件和mixin里有同名的钩子函数(比如created),都会执行,先执行mixin的,再执行组件自己的。
-
如果 data、methods、computed等名字重复,组件优先,即组件自己的会覆盖mixin的。
🔥总结一句话
mixin就是为了复用逻辑的,统一写一份,到处能用,减少重复劳动。
当我自己的组件中存在mounted
mounted
是 Vue 组件生命周期的一个钩子函数。
简单说就是:
当这个组件的 DOM 元素已经挂载到页面上了,就会自动执行 mounted 里的代码。
(你可以理解成:页面渲染完了,才可以安全操作 DOM了。)
<template>
<div>
<h2 @click="showName">学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
</div>
</template>
<script>
// 引入一个混合 hunhe
import {hunhe, hunhe2} from '../mixin'
export default {
name:'School',
data() {
return {
// msg:'我是一个武汉传媒学院的学生',
name:'武汉传媒学院',
address: '湖北武汉' ,
x:666
}
},
mixins:[hunhe, hunhe2],
// 对于生命周期钩子 是都要进行处理
mounted() {
console.log('雷猴啊!!!')
}
}
</script>
<template>
<div>
<h1>{{ msg }}</h1>
<h2 @click="showName">学生姓名:{{ name }}</h2>
<h2>学生性别:{{ sex }}</h2>
</div>
</template>
<script>
// 引入一个混合 hunhe 就是复用配置
import {hunhe, hunhe2} from '../mixin'
export default {
name:'Student',
data() {
return {
msg:'我是一个武汉传媒学院的学生',
name:'张三',
sex: '男'
}
},
mixins:[hunhe, hunhe2]
}
</script>
mixin.js也存在的时候:
// eslint-disable-next-line no-unused-vars
// 分别暴露
export const hunhe = {
methods: {
showName() {
alert(this.name)
}
},
mounted() {
console.log('你好啊')
}
}
export const hunhe2 = {
data() {
return {
x: 100,
y: 200
}
}
}
如果你用了 mixin,
mixin里面也有 mounted
,那么:
-
会先执行 mixin 的 mounted
-
再执行你组件自己的 mounted
-
顺序是 mixin优先,组件后执行
五、插件
什么是 插件(plugin)?
在 Vue 里,插件就是:
👉 把一些功能封装成一个可以全局使用的小模块,
👉 安装一次,整个应用任何地方都能用。
比如全局方法、全局指令、全局组件、Vue原型上挂点工具函数……都可以通过插件做到。
一句话总结:
插件就是扩展 Vue 功能的一个模块。
为什么要用插件?
-
功能独立,方便复用。
-
项目一大,需要统一管理一些功能(比如统一加日志、弹窗、请求封装等等)。
-
别人封装好了(比如 VueRouter、Vuex),直接用,省事又稳定。
插件怎么用?
插件一般有两步:
1. 写一个插件
一个 Vue 插件本质上就是一个对象,必须提供一个 install 方法。
比如自己写个简单的插件:
// myPlugin.js
export default {
install(Vue) {
// 添加全局方法
Vue.prototype.$hello = function() {
console.log('你好啊,我是插件扩展的方法')
}
// 也可以注册全局组件、全局指令等等
}
}
2. 使用插件
在入口文件(比如 main.js
)里使用 Vue.use()
安装插件:
import Vue from 'vue'
import App from './App.vue'
import myPlugin from './myPlugin'
Vue.use(myPlugin) // 安装插件
new Vue({
render: h => h(App),
}).$mount('#app')
这样,全局都可以用 this.$hello()
了,比如:
export default {
mounted() {
this.$hello() // 控制台打印:你好啊,我是插件扩展的方法
}
}
⚡注意几个关键点
-
插件必须暴露
install(Vue)
方法。 -
在 Vue 实例创建之前就要安装插件。
-
Vue.use()
只能调用一次(Vue自己会做防止重复注册)。
🔥总之一句话
插件就是给 Vue 增加全局功能的一种官方标准写法,用来提高可复用性、模块化管理。
自定义插件:
export default {
install(Vue,x,y,z){
console.log(x,y,z)
//全局过滤器
Vue.filter('mySlice',function(value){
return value.slice(0,4)
})
//定义全局指令
Vue.directive('fbind',{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
})
//定义混入
Vue.mixin({
data() {
return {
x:100,
y:200
}
},
})
//给Vue原型上添加一个方法(vm和vc就都能用了)
Vue.prototype.hello = ()=>{alert('你好啊')}
}
}
Vue.prototype
:这代表了 Vue 的原型对象。Vue 中的每一个实例都继承自Vue.prototype
,所以我们可以在这里添加一些方法或者属性,这样每个实例(即每个 Vue 组件)都可以通过this
访问到这些方法或属性。
作用:
-
全局可用:通过
Vue.prototype
,添加的方法可以在所有 Vue 实例中使用。这意味着你在任何一个组件中都可以直接通过this.hello()
来调用alert('你好啊')
,无论是 Vue 组件、根实例还是子组件。 -
访问方式:在 Vue 的组件中,可以像访问
this
上的任何其他方法一样,直接使用this.hello()
,因为hello
被添加到了 Vue 实例的原型上。
main.js 引入插件:
import Vue from 'vue'
import App from './App.vue'
// 引入插件
import plugins from './plugins'
// 关闭Vue生产提示
Vue.config.productionTip = false
// 插件的本质就是一个对象{} 里面还必须包含一个方法 insrall安装
// 应用(使用)插件
Vue.use(plugins, 1, 2, 3)
// 创建vm
new Vue({
el: '#app',
render: h => h(App)
})
六、 scoped样式
scoped
样式是 Vue 中的一种样式机制,用于将样式限制在当前组件内,而不会影响到其他组件。这意味着在 Vue 单文件组件中,<style scoped>
标签内的样式只会作用于当前组件的元素,不会影响到全局或其他组件的元素。
为什么使用 scoped
样式?
在开发大型应用时,我们通常希望每个组件的样式是隔离的,不会相互影响。为了实现这一目标,Vue 提供了 scoped
关键字,用来避免样式冲突。
School局部的
<!-- scoped 局部的 -->
<style scoped>
.demo {
background-color: skyblue;
}
</style>
scoped 在App里面使用 也只是在APP里面能够用
Student局部的
<style scoped>
.demo {
background-color: orange;
}
</style>