单文件组件

  • 推荐的命名方式

    • 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>