单文件组件
-
推荐的命名方式
-
School.vue
-
MySchool.vue
-
-
vue模板
<template>
</template>
<script>
export default {
}
</script>
<style>
</style>
- demo示例: 显示学校的名称,地址,按钮弹窗显示学校名称
- componen1.vue
<template>
<div class="demo">
<h3>名称:{{schoolName}}</h3>
<h3>地址:{{schoolAddress}}</h3>
<button type="button" @click="showMsg">测试</button>
</div>
</template>
<script>
export default { // 推荐使用这种导出方式(在模块导入的时候,方便书写)
name:School, // 取个名字
data(){
return {
schoolName:'xxx名称',
schoolAddress:'yyy地址'
}
},
methods:{
showMsg(){
alert(this.schoolName)
}
}
}
</script>
<style>
.demo {
background-color: #08C63E;
}
</style>
- 补充模块导出的三种方式
- export var data = {}
- export {data}
- export default data # 推荐
- 导入方式的两种写法
import ??? from ??? # 推荐,书写方便
import {???} from ???
- 定义组件小弟的'头',app组件
<template>
<div> <!--注意使用根元素包裹一下,不然会报错-->
<School></School>
</div>
</template>
<script>
import School from 'component1.vue'
export default {
name:App, // 别忘记取名
components:{
School
}
}
</script>
<style>
</style>
- 定义入口js文件,main.js
import App from 'app.vue'
new Vue({
el:'#container',
components:{
App
}
})
- 最后,应用到html文件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="container"> <!--应用-->
<App></App>
</div>
<!--两个js引用放到body最底部,如果放在header,有可能因为dom没来及渲染而报错~-->
<script src="../js/v2.6.10/vue.js"></script>
<script src="main.js"></script>
</body>
</html>
- 结果,报错
- Uncaught SyntaxError: Cannot use import statement outside a module
- 原因分析,浏览器无法识别vue文件这种语法,这是正常的
在vue脚手架的环境中,就可以正常运行~
- 小提醒,在上述html文件中,若想容器里面啥都不写,可以这搞
# demo.html
......
<body>
<div id="container"> <!--容器里面,啥都不写-->
</div>
<script src="../js/v2.6.10/vue.js"></script>
<script src="main.js"></script>
</body>
# main.js
import App from 'app.vue'
new Vue({
el:'#container',
template:`<App></App>` // 把组件写在这边
components:{
App
}
})
vue 脚手架(cli: command line interface)
-
相当于vue的IDE
-
先切换国内下载源
npm config set registry https://registry.npm.taobao.org
- 打开系统终端,安装vue-cli文件包: npm install -g @vue/cli
- 切换到要创建项目的目录,执行命令创建开发环境: vue create yourProjectName
- 进入项目目录,启动项目,测试是否搭建成功: npm run serve
- 入口文件 main.js 解析
import Vue from 'vue' // 引入vue
import App from './App.vue' // 引入App
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App) // 将 App组件放入容器中
}).$mount('#app')
把之前写的School组件运用到脚手架上
- 参考网址
https://blog.csdn.net/qq_45890970/article/details/123149612
- components目录下,新建 School.vue,把代码复制过来
<template>
<div class="demo">
<h3>名称:{{schoolName}}</h3>
<h3>地址:{{schoolAddress}}</h3>
<button type="button" @click="showMsg">测试</button>
</div>
</template>
<script>
export default {
name:'School',
data(){
return {
schoolName:'xxx名称',
schoolAddress:'yyy地址'
}
},
methods:{
showMsg(){
alert(this.schoolName)
}
}
}
</script>
<style>
.demo {
background-color: #08C63E;
}
</style>
- App.vue 注册组件
......
<script>
import HelloWorld from './components/HelloWorld.vue'
import School from './components/School.vue' // 引入
export default {
name: 'App',
components: {
HelloWorld,
School // 注册
}
}
</script>
- main.js无需更改
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
- 在执行npm run serve之前最好在vue.config.js 配置不检查语法错误(否则很可能会报错)
module.exports={
lintOnSave:false, //关闭语法检查
}
关于不同版本的vue
- vue.js 与 vue.runtime.xxx.js 的区别
- vue.js 是完整版的vue,包含 核心功能+模板解析器
- vue.runtime.xxx.js 是运行版本的vue,只有核心功能,没有模板解析器
- 不能使用 template 配置项
- 只能使用 render函数,使用 createElement 去渲染
导出脚手架配置命令
vue inspect > outputConfig.js
脚手架文件目录结构不建议修改的地方(5个)
- public
- favicon.ico
- index.html
- src
- main.js
- 如果想做一些个性化的配置,覆盖默认配置
可以在项目根目录新建 vue.config.js
// 替换默认配置,而没有写的配置项,仍然使用默认的值
module.exports = {......}
- 比如说,修改入口文件为'peiqi.js',不再是默认的'main.js'
module.exports = {
pages:{
index:{
entry:'src/peiqi.js'
}
}
}
ref属性介绍(相当于html id属性,又多了一点东东)
- 引入场景: 获取DOM元素
# school.vue
<template>
<div class="demo">
<h3>名称:{{schoolName}}</h3>
<h3>地址:{{schoolAddress}}</h3>
<h1 v-text="msg" id="msgText"></h1> <!--获取这个DOM元素-->
<button type="button" @click="showMsg">显示上方DOM</button> <!--通过按钮实现-->
</div>
</template>
<script>
export default {
name:'School',
data(){
return {
msg:'欢迎,欢迎', // 测试信息
schoolName:'xxx名称',
schoolAddress:'yyy地址'
}
},
methods:{
showMsg(){
// 使用原生js实现效果
// <h1 id="msgText">欢迎,欢迎</h1>
console.log(document.querySelector('#msgText'))
}
}
}
</script>
- 修改引入场景,不使用原生js,使用vue提供的ref属性实现
<template>
<div class="demo">
<h3>名称:{{schoolName}}</h3>
<h3>地址:{{schoolAddress}}</h3>
<h1 v-text="msg" ref="msgText"></h1> <!--把id替换成ref-->
<button type="button" @click="showMsg">显示上方DOM</button>
</div>
</template>
<script>
export default {
name:'School',
data(){
return {
msg:'欢迎,欢迎',
schoolName:'xxx名称',
schoolAddress:'yyy地址'
}
},
methods:{
showMsg(){
// this指向vc实例
console.log(this.$refs.msgText) // <h1>欢迎,欢迎</h1>
}
}
}
</script>
- 在组件上,使用 ref属性
# App.vue
<template>
<div id="app">
<School ref="sch"></School><!--新增ref属性-->
<button type="button" @click="testMsg">测试</button>
</div>
</template>
<script>
import School from './components/School.vue'
export default {
name: 'App',
components: {
School,
},
methods:{
testMsg(){
// 注意,this指向App组件实例
// this.$refs.sch 指向 School组件实例,不要搞混了...
console.log(this.$refs.sch)
}
}
}
</script>
- 如果使用原生js,输出的是DOM结构
<template>
<div id="app">
<!-- <School ref="sch"></School> -->
<School id="sch"></School> <!--原生id属性-->
<button type="button" @click="testMsg">测试</button>
</div>
</template>
<script>
......
export default {
......
methods:{
testMsg(){
// console.log(this.$refs.sch)
// <div class="demo" id="sch"><h3>名称:xxx名称</h3><h3>地址:yyy地址</h3><h1>欢迎,欢迎</h1><button type="button">显示上方DOM</button></div>
console.log(document.querySelector('#sch'))
}
}
}
</script>
- 使用ref属性的好处显而易见,获取该组件对象,后续可以做很多操作
ref属性小结
- 被用来给元素/子组件注册引用信息(id的替代者)
- 应用在html标签上获取的是真实DOM元素,应用在组件标签上市组件实例对象(vc)
- 使用方式:
- 打标识: <h1 ref="xxx">......</h1> 或 <School ref="xxx"></School>
- 获取: this.$refs.xxx
props配置项(属性)介绍
- 预留值,让使用者填充
### student.vue
<template>
<div>
<h1>{{msg}}</h1>
<h3>名字:{{name}}</h3>
<h3>年龄:{{age}}</h3>
</div>
</template>
<script>
export default {
name:'Student',
data(){
return {
msg:'欢迎,欢迎' // 只有msg有值
}
},
props:['name','age'] // name和age两个变量设置为'预填充'
}
</script>
......
### App.vue
<template>
......
<!--使用者填充值-->
<Student name="Jim Green" age=20></Student>
......
</template>
<script>
......
export default {
name: 'App',
components: {
HelloWorld,
School,
Student // 注册
},
......
</script>
- 注意事项: vc加上"props"配置项以后,配置项里面的值就是vc的属性,可以通过vc直接访问
<!--加上 href属性-->
<Student name="Jim Green" age=20 ref="stu"></Student>
......
methods:{
testMsg(){
console.log(this.$refs.stu) // vc实例
console.log(this.$refs.stu.name,this.$refs.stu.age) // 输出姓名,年龄
}
}
- 如果此时,想实现'age+1'这种运算,可以这么处理
### App.vue
<template>
......<!--动态绑定'age'属性变成':age'(如不这样处理,值传过去就是字符串,无法实现加1运算)-->
<Student name="Jim Green" :age=20 ref="stu"></Student>
</template>
### student.vue
<template>
<div>
......
<h3>年龄:{{age+1}}</h3> <!--加1运算-->
</div>
</template>
- 还可以限制'预留值'类型
<template>
<div>
<h1>{{msg}}</h1>
<h3>名字:{{name}}</h3>
<h3>年龄:{{age+1}}</h3>
</div>
</template>
<script>
export default {
......
// props:['name','age']
props:{
name:String,
age:Number // 限定传入值的类型
}
}
</script>
- 进一步拓展,可以限制'是否必传','默认值'
### student.vue
<template>
<div>
<h1>{{msg}}</h1>
<h3>名字:{{name}}</h3>
<h3>年龄:{{age+1}}</h3>
</div>
</template>
<script>
export default {
......
props:{
name:{
type:String,
required:true // 是否必传
},
age:{
type:Number,
default:16 // 默认值(前提是没传值,有传值的话该值无效)
}
}
}
</script>
### App.vue
......
<!--age可以不传,使用默认值16-->
<Student name="Jim Green" ref="stu"></Student>
- 尝试修改传过来的值: 能改,但是有警告,不要这样去做
有可能使vue产生奇奇怪怪的问题
### student.vue
<template>
<div>
......
<h3>年龄:{{age+1}}</h3>
<!--尝试修改age值-->
<button type="button" @click="updateAge">尝试修改age值</button>
</div>
</template>
<script>
export default {
......
props:['name','age'],
methods:{
updateAge(){
this.age = 100; // 修改age值
}
}
}
</script>
- 推荐的解决办法,使用一个变量去接收传过来的值
我们修改这个变量就好,而不去动传过来的值
<template>
<div>
......
<!--myAge-->
<h3>年龄:{{myAge+1}}</h3>
<button type="button" @click="updateAge">尝试修改age值</button>
</div>
</template>
<script>
export default {
name:'Student',
data(){
return {
msg:'欢迎,欢迎',
myAge:this.age // 接收age值
}
},
props:['name','age'],
methods:{
updateAge(){
this.myAge = 100; // 使用myAge修改,而不去动age
}
}
}
</script>
props配置项小结
- 功能: 让组件接收外部传过来的数据
- 传递数据: <Demo name='xxx'/>
- 接收数据
- 只接收: props:['name']
- 限制类型:
props:{
age:Number
}
- 限制类型,限制必要性,默认值
props:{
name:{
type:String,
required:true
},
age:{
type:Number,
default:16
}
}
- 注意事项
- props是只读的,vue底层会监测到你对props的修改
如果进行修改,就会发出警告
因为业务需求真的需要修改,请使用data中的变量去接收
然后修改data中的数据
mixin介绍(混合,可以理解为'共享')
- 测试环境: 自定义两个组件,有类似的弹框逻辑
### Student.vue
<template>
<div>
<h3>名字:{{name}}</h3>
<h3>性别:{{sex}}</h3>
<button type="button" @click="showMsg">显示学生姓名</button>
</div>
</template>
<script>
export default {
name:'Student',
data(){
return {
name:'Jim Green',
sex:'male'
}
},
methods:{
showMsg(){
alert(this.name) // 输出姓名
}
}
}
</script>
......
### School.vue
<template>
<div class="demo">
<h3>名称:{{name}}</h3>
<h3>地址:{{address}}</h3>
<button type="button" @click="showMsg">显示学校名称</button>
</div>
</template>
<script>
export default {
name:'School',
data(){
return {
name:'xxx名称',
address:'yyy地址'
}
},
methods:{
showMsg(){
alert(this.name) // 输出学校名称
}
}
}
</script>
<style>
.demo {
background-color: #08C63E;
}
</style>
- 上述两个组件中,弹框逻辑类似,所以,可以考虑整合在一起处理
- 把公共部分的逻辑单独分离出来
### mixin.js(和main.js同级目录)
export const mixin = {
methods:{
showMsg(){
alert(this.name)
}
}
}
- 组件引入并使用
### School.vue
<script>
import {mixin} from '../mixin.js' // 引入
export default {
name:'School',
data(){
return {
name:'xxx名称',
address:'yyy地址'
}
},
// methods:{ // 原来的逻辑可注释掉
// showMsg(){
// // console.log(document.querySelector('#msgText'))
// // console.log(this.$refs.msgText)
// alert(this.name)
// }
// },
mixins:[mixin] // 使用
}
</script>
### Student.vue(一样的用法)
<template>
<div>
<h3>名字:{{name}}</h3>
<h3>性别:{{sex}}</h3>
<button type="button" @click="showMsg">显示学生姓名</button>
</div>
</template>
<script>
import {mixin} from '../mixin.js' // 引入
export default {
name:'Student',
data(){
return {
name:'Jim Green',
sex:'male'
}
},
mixins:[mixin] // 使用
}
</script>
- 若在 mixin.js 挂载事件,由于被组件使用了两次,所以会被执行两次
### mixin.js
export const mixin = {
methods:{
......
},
mounted(){ // 新增
console.log('Hello!') // Student.vue 和 School.vue 分别调用一次
}
}
- 若在 mixin.js 挂载事件,在组件中也挂载相同的事件,结果就是'都执行'
### mixin.js
export const mixin = {
methods:{
......
},
mounted(){ // 被执行两次
console.log('Hello!')
}
}
### School.vue
......
<script>
import {mixin} from '../mixin.js'
export default {
name:'School',
data(){
......
},
mixins:[mixin],
mounted(){
console.log('Hello!School!!') // 被执行一次
}
}
</script>
### Student.vue
......
<script>
import {mixin} from '../mixin.js'
export default {
name:'Student',
data(){
......
},
mixins:[mixin],
mounted(){
console.log('Hello!Student!!') // 被执行一次
}
}
</script>
-
若 mixin 和 组件 公有变量分析
-
若 mixin 有而 组件没有,就使用 mixin 的变量
-
若 mixin 有而 组件也有,就使用 组件 的变量
-
- mixin 有而 组件没有
### mixin.js
export const mixin = {
data(){
return {
x:100, // 定义两个变量
y:200
}
},
methods:{
......
},
}
### School.vue
<script>
import {mixin} from '../mixin.js'
export default {
......
data(){
return {
name:'xxx名称',
address:'yyy地址'
}
},
mixins:[mixin],
mounted(){
console.log('school',this.x,this.y) // school,100,200
}
}
</script>
- mixin 有而 组件也有
### mixin.js
export const mixin = {
data(){
return {
x:100, // 定义两个变量
y:200
}
},
methods:{
......
},
}
### School.vue
<script>
import {mixin} from '../mixin.js'
export default {
name:'School',
data(){
return {
name:'xxx名称',
address:'yyy地址',
x:300, // 新增
y:400
}
},
mixins:[mixin],
mounted(){
console.log('school',this.x,this.y) // school,300,400
}
}
</script>
- 定义全局mixin,所有组件都可以使用,无需在各个组件里面注册
### mixin.js
export const mixin = {
methods:{ // 简单定义一个公有方法
showMsg(){
alert(this.name)
}
},
}
### main.js
import Vue from 'vue'
import App from './App.vue'
import {mixin} from './mixin.js' // 导入
Vue.config.productionTip = false
Vue.mixin(mixin) // 运用
new Vue({
render: h => h(App),
}).$mount('#app')
- 现在, School组件和Student组件都可以使用这个方法了
- 小结: mixin 可以把多个组件的共同配置提取成一个混入对象
- 使用方式
- 定义 mixin
{
data(){...},
methods:{...}
......
}
- 使用
- 全局混入: Vue.mixin(xxx)
- 局部混入: mixins:[xxx]
插件: 拓展Vue的功能(外挂)
### plugins.js(与main.js同目录)
export default {
install(){ // 必须实现 install 配置项,先简单实现逻辑
console.log('插件测试')
}
}
### main.js
import Vue from 'vue'
import App from './App.vue'
import plugins from './plugins.js' // 导入
Vue.config.productionTip = false
Vue.use(plugins) // 使用插件
new Vue({
render: h => h(App),
}).$mount('#app')
- 插件最最重要的参数,Vue构造函数(类对象)
有了它,可以做非常多的事情
### plugins.js
export default {
install(Vue){ // 接收
console.log('插件测试',Vue)
// 可以做出非常牛的功能
// Vue.filter()
// Vue.directive()
// Vue.mixin()
// Vue.prototype.hello=...
}
}
- 小结
- 功能: 增强Vue
- 本质: 是一个包含install方法的对象,install第一个参数是Vue
第二个参数是插件使用者传递的数据
- 定义和使用
export default {
install(Vue){ // 接收
console.log('插件测试',Vue)
// 可以做出非常牛的功能
// Vue.filter()
// Vue.directive()
// Vue.mixin()
// Vue.prototype.hello=...
}
}
Vue.use(xxx)
scoped 属性介绍: 限制组件的css作用域(只在当前组件生效)
- 引入场景: 当两个组件的部分css样式相同的时候
谁在App.vue中最后被引入,样式就用谁的
### School.vue
<template>
<div class="demo"> <!--定义样式-->
......
</div>
</template>
<script>
export default {
......
}
</script>
<style>
.demo {
background-color: #08C63E;
}
</style>
### Student.vue
<template>
<div class="demo"> <!--样式名称和School.vue相同-->
......
</div>
</template>
<script>
export default {
......
}
</script>
<style>
.demo {
background-color: skublue;
}
</style>
### App.vue
......
<script>
import HelloWorld from './components/HelloWorld.vue'
import School from './components/School.vue'
import Student from './components/Student.vue' // 后引入,就用它的样式
export default {
......
}
</script>
-
解决办法
-
修改样式名称,不同即可(组件很多的时候,处理起来不方便)
-
使用 scoped 限制css样式只在当前组件范围内生效
-
### School.vue
<style scoped>
.demo {
background-color: #08C63E;
}
</style>
### Student.vue
<style scoped>
.demo {
background-color: skublue;
}
</style>
- 原理解析: 在html标签上,生成属性随机码,在这个随机码(保证唯一,不冲突)上面应用样式
<div data-v-123xxx> <!--School组件样式-->
<div data-v-456yyy> <!--Student组件样式-->
- scoped样式小结
- 让样式在局部生效,防止冲突
- 写法: <style scoped>