目录
vue脚手架vue-cli
vue-cli
安装与使用
- (仅第一次执行)全局安装@vue/cli:
npm install -g @vue/cli
,安装好了之后,就多了个vue
命令
- 切换到你要创建项目的目录,然后使用命令创建项目:
vue create xxx
- 启动项目:
npm run serve
,执行该命令后,vue会直接运行main.js
脚手架结构
.gitignore:git的忽略文件,哪些文件不想被git管理,在这里配好
babel.config.js:Babel的控制文件
package-lock.json和package.json:只要你打开的工程符合npm规范,那么一定有这两个文件,他们是包的说明书,前者是包版本控制/锁定文件。指令介绍:
"scripts": {
"serve": "vue-cli-service serve",/*开发时使用这个命令,让别人帮你配置服务器,帮你把东西都弄好*/
"build": "vue-cli-service build",/*代码写完了,功能开发完了,想把整个项目变成浏览器认识的文件*/
"lint": "vue-cli-service lint"/*对.js和.vue文件进行语法检查*/
},
render函数
main.js作为入口文件,我们在里面创建vue实例时,写的以下代码:
new Vue({
el:'#app',
render: h => h(App)
})
我们以前创建vue实例时,写的代码如下,如果我们还是写下面这个代码会报错,这是为什么呢?
new Vue({
el:'#app',
template:`<h1>你好啊</h1>`,
components:{App},
})
因为我们引入vue(import Vue from 'vue'
)使用的是ES6模块化引入,此时引入的是..\node_modules\vue\dist\vue.runtime.esm.js
,该vue残缺了模板解析器,意味着没人给你解析new Vue()中的template配置项,你必须使用render函数。
引入残缺版vue,但是我还想配置内容,就要使用render函数,render函数是vue帮你调用的,render函数将App组件放入容器中,render函数必须有返回值,返回值就是你想渲染的具体内容,render函数接收一个参数createElement,该参数createElement是个函数,借助该函数createElement渲染具体的内容。createElement('标签名','标签体内容')
或createElement(组件名)
render(createElement){
return createElement('h1','你好啊')
}
没有用到this,可以写成箭头函数,箭头函数只有一个参数,可以省略(),只有一条语句并且是返回值,可以省略return和{},createElement是形参,可以用其他变量代替,如h,那么上面代码可以精简为:render: h => h('h1','你好啊')
。等同于:template':<h1>你好啊</h1>'
综上:
- 如果你引入的时残缺版的vue,因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。那么新建vue实例时代码如下:(推荐)
import Vue from 'vue'
import App from './App.vue'//引入App组件,它是所有组件的父组件
new Vue({
el:'#app',
render: h => h(App),// 这个App是个变量,他会去上面找,就找到了你上面引入的那个组件
})
- 如果你引入的是完整版的vue,那么新建vue实例时代码如下:
import Vue from 'vue/dist/vue.js'
import App from './App.vue'//引入App组件,它是所有组件的父组件
new Vue({
el:'#app',
template:`<h1>你好啊</h1>`,
components:{App},
})
完整版vue.js与残缺版vue.runtime.xxx.js的区别:
- vue.js是完整版的Vue,包含:核心功能(声明周期、处理事件等功能)+模板解析器(解析new Vue()时传入的template配置项)。
- vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
为什么要设计精简版vue:如果只有完整版vue的话,在vue源码中,模板解析器代码的体积占了vue的1/3,有天你代码写完了,需要交给webpack打包,webpack打包完了,会生成一个非常大的文件,该文件中肯定包含vue,也就包含模板解析器的代码,但此时乃至以后,都用不上模板解析器了,因为在开发时,模板解析器帮我们翻译模板,但现在我们的代码写完了,借助webpack已经可以把vue翻译成.js、.css、.html了,而且那些该解析的模板都解析完了,变成了浏览器认识的文件,此时模板解析器就没什么作用了,但是他还在打包后的文件中。所以就出现了精简版vue,打包后的文件能节约一些空间。
举一个生活中的例子:假如你家要装修,要铺瓷砖,你有以下两种方案:
- 买瓷砖(vue核心)+买工人(模板解析器),瓷砖铺好后你得到了铺好的瓷砖和工人,你以后还要养着他们,没必要啊,他们以后没用了
- 买瓷砖(vue核心)+雇工人(模板解析器),瓷砖铺好后你得到了铺好的瓷砖
修改脚手架的默认配置
Vue脚手架隐藏了所有webpack相关的配置,若想查看具体的webpack配置,请执行:vue inspect > output.js
如果你采用脚手架的默认配置,以下文件/文件夹名你不能改:public、index.html、src、main.js。但是如果你非要改,在package.json保存的地方新建vue.config.js(他们两个文件要同级),vue.config.js代码如下:
//vue最终会把vue.config.js输送给webpack,webpack是基于Node的,所以vue.config.js中使用commonJS。
module.exports = {
pages: {
index: {
entry: 'src/main.js',//入口
},
},
lintOnSave:false, //关闭语法检查
}
修改package.json后一定要重新npm run serve
。
其余配置参考官方配置参考。
其实,脚手架会把vue.config.js中的配置与webpack中已经写好的配置进行合并,某个配置如果vue.config.js中有,则使用vue.config.js的,否则使用webpack中已经写好的,这样程序员是碰不到核心文件的。
ref属性
需求:点击按钮获取某个DOM元素。
vue给我们提供了ref属性,用于给元素或子组件注册引用信息(id的替代者),即给DOM节点打标识。用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
用法:
- 打标识:
<h1 ref="xxx">.....</h1> 或 <School ref="xxx"></School>
- 获取:
this.$refs.xxx
VueComponent会把所有包含ref的节点收集到自身的$refs
属性中,如下图。$refs
的值是一个对象,键是ref的值,值是该ref的节点。
app.vue中代码如下:
<template>
<div>
<h1 v-text="msg" ref="title"></h1>
<button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
<School ref="sch"/>
<School id="sch"/>
</div>
</template>
<script>
//引入School组件
import School from './components/School'
export default {
name:'App',
components:{School},
data() {
return {
msg:'欢迎学习Vue!'
}
},
methods: {
showDOM(){
console.log(this.$refs.title) //真实DOM元素
console.log(this.$refs.btn) //真实DOM元素
console.log(this.$refs.sch) //School组件的实例对象VueComponent(vc)
console.log(document.getElementById('sch'))//School组件对应的完整的dom结构
}
},
}
</script>
school.vue中代码如下:
<template>
<div class="school">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</template>
<script>
export default {
name:'School',
data() {
return {
name:'尚硅谷',
address:'北京·昌平'
}
},
}
</script>
<style>
.school{
background-color: gray;
}
</style>
使用ref和使用id的区别:对于传统的HTML标签来说,二者只有写法和用法上的区别,但是对于组件标签来说,<School ref="sch"/>
得到的是School组件的实例对象VueComponent。如下图
<School id="sch"/>
得到的是School组件对应的完整的dom结构,如下图
props配置
props:让组件接收外部传过来的数据。
父组件给子组件传数据:通过给子组件的标签写属性的方式传入数据:数据名="值"
,一定要加""
app.vue代码:
<template>
<div>
<!--:age="18":age的值是运行""中的内容得到的结果,就是数字18,""中的内容当成了表达式执行-->
<Student name="李四" sex="女" :age="18"/>
</div>
</template>
<script>
import Student from './components/Student'
export default {
name:'App',
components:{Student}
}
</script>
子组件使用props接收父组件传入的数据,使用props接收到的数据保存在了本组件实例对象身上,如果你在子组件实例对象身上的props中声明了一个变量,但是父组件没给你传该变量,那么多余变量的值为undefined
Student.vue中代码:
<template>
<div>
<!-- 在这里你相当于就在组件实例对象上了,可以使用组件实例对象身上的所有东西 -->
<h1>{{msg}}</h1>
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<h2>学生年龄:{{myAge+1}}</h2>
<button @click="updateAge">尝试修改收到的年龄</button>
</div>
</template>
<script>
export default {
name:'Student',
data() {
console.log(this)
return {
msg:'我是一个尚硅谷的学生',
// 接收到的props不允许改,控制台会报错,但是还是能修改成功.那么如果我们想修改接收到的数据,
// 可以在data中定义个变量接收接收到的数据,后续改data中的变量即可
myAge:this.age
// name:'cara'//在data中定义个name变量,在props中定义个name变量用于接收父组件传过来的数据
// 两个name都会被放到组件实例对象上,会发生冲突,控制台会报错,props的name变量优先级更高,
// 以外部传进来的数据为主,也就是说props的变量优先被组件实例接收,优先被放在组件实例身上
}
},
methods: {
updateAge(){
this.myAge++
}
},
//简单声明接收,使用props接收到的数据保存在了本组件实例对象身上
//如果你这里声明了一个变量,但是父组件没给你传,那么多余变量的值为undefined
// props:['name','age','sex']
//接收的同时对数据进行类型限制,props写成对象,里面是一对对的键值对,键是数据名,值是JS内置对象
/* props:{
name:String,
age:Number,
sex:String
} */
//接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
props:{
//required和default不能同时存在
name:{
type:String, //name的类型是字符串
required:true, //name是必要的
},
age:{
type:Number,
default:99 //默认值
},
sex:{
type:String,
required:true
}
}
}
</script>
mixin(混入)
混入:可以把多个组件共用的配置提取成一个混入对象
需求:点击“姓名”,弹窗弹出姓名。
现在我们有两个组件School和Student,他们中间有些代码一样
那我们可以把这些一样的代码提取到一个文件中,在School和Student使用混入直接使用这些相同的代码。新建mixin.js,将相同代码提取并暴露。注意:此处为了后续演示,我们还添加了生命周期函数和data。
export const hunhe = {
methods: {
showName(){
alert(this.name)
}
},
mounted() {
console.log('你好啊!')
},
}
export const hunhe2 = {
data() {
return {
x:100,
y:200
}
},
}
混入原则:
- 对于data中的数据、methods中的方法,混合中有的,你这里也有,那就使用你这里的,混合中有的,你这里没有,那就是用混合中的
- 对于生命周期函数,混合中有的和你这里有的都会用,并且优先使用混合中的。
有两种使用混入的方式:
- 在【用到混入的组件中引入并配置mixins】即可,如果只有Student组件使用,Student组件代码如下
<template>
<div>
<h2 @click="showName">学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
</div>
</template>
<script>
import {hunhe,hunhe2} from '../mixin'//局部引入一个hunhe
export default {
name:'Student',
data() {
return {
name:'张三',
sex:'男'
}
},
mixins:[hunhe,hunhe2]
}
</script>
- 如果有很多组件乃至所有的组件都要用到混入中的代码,那么在main.js中全局引入并注册混入,main.js中代码:
import Vue from 'vue'//引入Vue
import App from './App.vue'//引入App
import {hunhe,hunhe2} from './mixin'//全局引入混合
//这样写,在你整个应用中,所有VC和VM都会得到这两个混合
Vue.mixin(hunhe)
Vue.mixin(hunhe2)
new Vue({//创建vm
el:'#app',
render: h => h(App)
})