首先,先说正常情况下,应该是这样操作:
//创建Vue的实例对象
new Vue({
//指定容器app
el:"#app",
template:` `,
components:{
App
}
})
换句话说,导入的Vue文件有问题!其实Vue的js文件有很多版本,只不过我们导入的Vuejs文件是一个精简版(缺少部分功能的Vue)。
而上面的vue.runtime.esm.js文件,就残缺了模板解析器,没有了模板解析器就没法解析template中的内容了。
完整版的vue.js,在第三方库的dist下面。
这样我们就直接导入完整版的vue.js就可以直接运行项目了,也并不会报错。
这样确实可以解决,但是不会被推荐的!因为我们要用render函数配合vue默认的js文件操作。
render函数很关键!它会得到一个参数叫做createElement参数,这个参数是一个专门用来创建元素的函数。
import Vue from 'vue'
// import App from './App.vue'
Vue.config.productionTip = false
new Vue({
el:"#app",
//render函数有一个参数:createElement也是一个函数,作用就是专门用来创建元素的。
render(createElement){
console.log(typeof createElement);
//例如:我们返回一个createElement创建的h1标签
console.log(createElement('h1','你好啊'));
return createElement('h1','你好啊');
}
})
换句话说,vue默认导入的vuejs文件是没有模板解析器的,而render中的createElement参数帮他做了这件事情。
然后,将render函数进行简化一下,得到的就是vue开始的那种样式:
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
el:"#app",
//render函数有一个参数:createElement也是一个函数,作用就是专门用来创建元素的。
//render(createElement){
// console.log(typeof createElement);
// //例如:我们返回一个createElement创建的h1标签
// console.log(createElement('h1','你好啊'));
// return createElement('h1','你好啊');
//}
//函数式写成箭头函数,一个参数,一个返回值都可以省略括号或中括号。然后将createElement换成h字符就完成最简化版了。
render:h=>h(App)
//有人疑惑这里为什么不是'App',因为dom中没有App标签。但是我们上面引入了App变量,这样就可以读取了。
})
Vue的分为两部分一个是核心文件 (什么生命周期等等的), 一个是模板解析器。
模板解析占很大一部分体积(大约三分之一),如果将模板解析器移除,仅仅用render函数操作就非常方便操作。省空间省代码。
Vue当中默认使用的是vue_runtime.esm.js文件,runtime就是运行时Vue;esm是ES module,就是如果使用ES6语法我们可以用这个包操作。
我们的组件中有template标签,既然我们设置的vuejs文件没有解析器,那么怎么解析这种标签类型的template呢?Vue也给它指定了template标签解析器,这个解析器专门用来解析.vue文件中的template标签。
Vue脚手架的配置文件是有的,只不过隐藏到了别的地方而已。
Vue脚手架隐藏了所有的webpack相关的配置,若想看具体的webpack配置,可以执行:vue inspect > output.js命令。
以下五个部分文件名是不能随便改的,这是这是vue脚手架的硬性要求。
对于这种配置的相关内容,都是先用现查就可以。这么多配置不可能记住的咱们只需要改的时候去官方看看操作和配置就可以了。
关闭语法检查,这个语法检车确实烦人,想关闭也可以配置vue脚手架环境。
正常我们原生的js获取dom元素使用的getelementbyid方法。
<template>
<div>
<h1 v-text="msg" id="title">h1>
<button @click="showDOM">点我输出上方的DOM元素button>
<School/>
<School/>
div>
template>
<script>
import School from "./components/School.vue"
export default{
name:'App',
components:{School},
data(){
return {
msg:"欢迎学习Vue!"
}
},
methods:{
showDOM(){
//正常我们打印dom元素使用原生的document.getElementById("title")
console.log(document.getElementById("title"));
}
}
}
script>
<style>
style>
ref属性:
<template>
<div>
<h1 v-text="msg" ref="title">h1>
<button @click="showDOM">点我输出上方的DOM元素button>
<School ref="sch"/>
div>
template>
<script>
import School from "./components/School.vue"
export default{
name:'App',
components:{School},
data(){
return {
msg:"欢迎学习Vue!"
}
},
methods:{
showDOM(){
//这里的this指向的是当前组件的Vuecomponent函数实例对象。
console.log(this)
//Vuecomponent实例对象中有一个$refs属性,里面就包含着ref的信息。
console.log(this.$refs)
console.log(this.$refs.title)
console.log(this.$refs.sch)
}
}
}
script>
<style>
style>
props配置项使用的演示:
<template>
<div class="school">
<h1>{{msg}}h1>
<h2>学生姓名: {{name}}h2>
<h2>学生性别: {{sex}}h2>
<h2>学生年龄: {{age}}h2>
<button @click="show">按钮button>
div>
template>
<script>
export default{
name:"Student",
data(){
return {
msg:"清华大学的学习",
}
},
props:['name','sex','age'],
methods:{
show(){
console.log(this)
}
}
}
script>
<style>
.school{
background-color: gray;
}
style>
这样可以直接在父组件中定义,相关属性的配置:
<template>
<div>
<Student name="李四" sex="女" age="18"/>
<Student name="王五" sex="男" age="20"/>
div>
template>
<script>
import Student from "./components/Student.vue"
export default{
name:'App',
components:{Student},
}
script>
<style>
style>
正常在vue的操作方式,使用v-bind来解决,简写为 ': ’ 号 。
正常情况下,属性里面的值,就是一种字符串形式。
但是,如果给属性加上了v-bind指令,那么这个属性里面的值,就会被作为js表达式来处理。这样也就完美解决了相关操作。
<template>
<div class="school">
<h1>{{msg}}h1>
<h2>学生姓名: {{name}}h2>
<h2>学生性别: {{sex}}h2>
<h2>学生年龄: {{age+1}}h2>
<button @click="show">按钮button>
div>
template>
<script>
export default{
name:"Student",
data(){
return {
msg:"清华大学的学习",
}
},
methods:{
show(){
console.log(this)
}
},
//props:['name','age','sex'] // 简单声明接受
//接受的同时对属性进行类型限制
/* props:{
//使用这种方式来限制类型
name:String,
age:Number,
sex:String
}
*/
//接受的同时对数据:进行类型限制+默认值的指定+必要性的限制
props:{
name:{
type:String, //name的类型是字符串
required:true, // 名字是必须要传入的
},
age:{
type:Number,
default:99, //设置默认值99
},
sex:{
type:String,
required:true,
}
}
}
script>
<style>
.school{
background-color: gray;
}
style>
但是如果想要修改props配置项的内容,有需求要求这么做!我们可以通过一个data配置项中的属性来间接操作就好了。
<template>
<div class="school">
<h1>{{msg}}h1>
<h2>学生姓名: {{name}}h2>
<h2>学生性别: {{sex}}h2>
<h2>学生年龄: {{myAge}}h2>
<button @click="updateAge">修改学生年龄button>
div>
template>
<script>
export default{
name:"Student",
data(){
return {
msg:"清华大学的学习",
//通过加一个myAge来达到修改页面属性的效果。
myAge:this.age
}
},
methods:{
updateAge(){
this.myAge = 99
}
},
//props:['name','age','sex'] // 简单声明接受
//接受的同时对属性进行类型限制
/* props:{
//使用这种方式来限制类型
name:String,
age:Number,
sex:String
}
*/
//接受的同时对数据:进行类型限制+默认值的指定+必要性的限制
props:{
name:{
type:String, //name的类型是字符串
required:true, // 名字是必须要传入的
},
age:{
type:Number,
default:99, //设置默认值99
},
sex:{
type:String,
required:true,
}
},
}
script>
<style>
.school{
background-color: gray;
}
style>
mixin混入的作用:可以把多个组件共用的配置提取成一个混入对象处理。
在外部创建一个js文件:
//分别暴露一下
export const mixin = {
methods:{
showName(){
alert(this.name)
}
},
mounted(){
console.log("我是外部js的mounted")
}
}
export const mixin2 ={
data(){
return {
x:100,
y:200
}
}
}
之后,由mixins配置项引入:
<template>
<div class="school">
<h2 @click="showName">学生姓名: {{name}}h2>
<h2>学生性别: {{sex}}h2>
div>
template>
<script>
//引入一个minin.js文件的minin(分别暴露)
import {mixin,mixin2} from '../mixin.js'
export default{
name:"Student",
data(){
return {
name:"张三",
sex:"男"
}
},
mixins:[mixin,mixin2],
mounted(){
console.log("我是组件的mounted")
}
}
script>
<style>
.school{
background-color: gray;
}
style>
script>
<style>
.school{
background-color: gray;
}
style>
注意:
全局混合是直接在main.js中进行配置:直接通过Vue.mixin(xxx)来全局配置了。
import Vue from "vue"
import App from "./App.vue"
import {mixin,mixin2} from "./mixin.js"
Vue.config.productionTip = false;
Vue.mixin(mixin)
Vue.mixin(mixin2)
new Vue({
el:'#app',
render:h=>h(App)
})
vue插件的作用:增强Vue。
实际上,vue插件就是一个包含install方法的一个对象,install方法的第一个参数是Vue构造函数,第二个以后的参数是插件使用者传递的数。
再简单的说就是,vue的插件是从外部js文件引入,并且这个插件要有一个install函数方法。
Vue想要使用该插件只需操作两部分:
例如定义一个plugins.js插件文件:
export default {
//install函数式有参数的,并且这个参数就是Vue实例对象的创造者,Vue的构造函数。
install(Vue){
console.log("参数:",Vue)
}
}
在main.js中引入插件并且应用插件:
import Vue from "vue"
import App from "./App.vue"
Vue.config.productionTip = false;
//引入插件
import plugins from './plugins.js'
//应用插件
Vue.use(plugins)
new Vue({
el:'#app',
render:h=>h(App)
})
主要平时设置全局配置,甚至修改Vue原型的属性或添加方法都可以使用插件来做到。
并且,我们在组件中,只需要调用对应全局配置的函数就可以了。
组件之间样式的问题!
两个组件的样式,比如样式类选择器的名字相同,那么会出现一个覆盖的效果。
由于Vue把样式放到一起,那么一个组件就会覆盖另一个组件的样式!至于谁覆盖谁这和导入组件的顺序有关!
对于上面的问题就需要使用scoped(英文翻译:局部的)属性:
lang属性就是language,对于样式有很多语言,一般使用css,还有less等等,默认也是css。
我们可以在style的lang属性声明当前的样式使用什么语言来编译的。
对于less解析器,是需要安装的:
npm view less-loader versions命令
npm view webpack versions命令
npm i less-loader@7 命令(安装7版本的less-loader)
像什么less都有严格的版本对应webpack的版本的!!!因此不能随便安装的!
分析结构的话,大体就像下面的方式:一层一层的分析。
对于一个整体页面,给他拆分为数组,是非常容易的。
对于这些动态数据存储的类型,名称等等,一般我们通过数组或对象的形式存储。
NanoID的作用和uuid一样,生成唯一字符串。在javascript中,我们可以使用NanoID来生成唯一的id对象。
nodejs中安装NanoID,执行npm -i nanoid。之后在import {nanoid} from ‘nanoid’(分别暴露)就可以使用了。
想要子组件向父组件传递值,就可以在父组件中创建一个函数,将该函数传给子组件(子组件拿到就出现在了vuecomponent上面)。此时的函数依然在父组件中,当在子组件调用该组件传递值时,函数就会在父组件收到。
注意事项:通过父组件传值的各个method,computed等等都不能重名,因为会冲突!如一下错误:
vue中要求是不能修改props配置项的东西,但是对于配置项中的对象而言,不能修改对象本身,可以修改对象中的属性!这是一个误区!
Window.confirm() 方法显示一个具有一个可选消息和两个按钮(确定和取消)的模态对话框 。
reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
MyFooter.vue文件:
<template>
<div class="todo-footer" v-show="total">
<label>
<input type="checkbox" :checked="isAll" @change="checkAll"/>
label>
<span>
<span>已经完成{{doneTotal}} / 全部:{{total}}span>
span>
<button class="btn btn-danger" @click="clearAll">清除已完成任务button>
div>
template>
<script>
export default {
name:"MyFooter",
props:['todoList','checkAllTodoObj','clearAllTodoObj'],
computed:{
total(){
return this.todoList.length
},
doneTotal(){
//reduce的使用:
//第一个参数是函数,当前todoList的数组长度为多少,就调用多少次。
//第二个参数是开始的时候的pre的起始值。
const x = this.todoList.reduce((pre,current)=>{
//这里的pre参数是上一次执行的返回W值。起始索引是0。
// console.log('pre参数:',pre)
//这里的current参数是这次执行的对象。
// console.log('current参数:',current)
return pre + (current.done ? 1:0)
},0)
// console.log("reduce的最终返回:",x)
return x;
},
isAll(){
return this.doneTotal == this.total && this.total > 0
}
},
methods:{
checkAll(e){
// console.log(e.target.checked)
this.checkAllTodoObj(e.target.checked)
},
clearAll(){
if(confirm("确定清除全部任务吗?"))
this.clearAllTodoObj()
}
}
}
script>
<style scoped>
.todo-footer{
height: 40px;
line-height: 40px;
padding-left: 6px;
margin-top: 5px;
}
.todo-footer label{
display: inline-block;
margin-right: 20px;
cursor: pointer;
}
.todo-footer label input{
position: relative;
top:-1px;
vertical-align: middle;
margin-right: 5px;
}
.todo-footer button{
float: right;
margin-top: 5px;
}
style>
MyHeader.vue文件:
<template>
<div class="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车确认" @keyup.enter="add">
div>
template>
<script>
import {nanoid} from 'nanoid'
export default {
name:"MyHeader",
methods:{
add(e){
//判断是否为空
if(!e.target.value.trim())
return alert('输入不能为空!')
//获取用户输入信息
console.log(e.target.value)
//包装用户信息,id使用Nanoid来操作
const todoObj = {
id:nanoid(),
title:e.target.value,
done:false
}
//当前vc拿到receive方法,直接传值就可以了。
this.receive(todoObj)
e.target.value = ''
}
},
props:['receive']
}
script>
<style scoped>
.todo-header input {
width: 560px;
height: 28px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 7px;
}
.todo-header input:focus {
outline: none;
border-color: rgba(82,168,236,0.8);
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
}
style>
MyItem.vue文件:
<template>
<li>
<label>
<input type="checkbox" :checked="itemPro.done" @click="handleCheck(itemPro.id)"/>
<span>{{itemPro.title}}span>
label>
<button class="btn btn-danger" @click="handleDelete(itemPro.id)">删除button>
li>
template>
<script>
export default {
name:"MyItem",
//声明接受todo对象
props:['itemPro','checkTodoObj','deleteTodoObj'],
mounted(){
// console.log(this.itemPro);
},
methods:{
//勾选or取消
handleCheck(id){
//通知App组件将对应的itemPro对象的done值取反
this.checkTodoObj(id)
},
//删除
handleDelete(id){
//根据用户
if(confirm('确定删除吗?')){
this.deleteTodoObj(id)
}
}
}
}
script>
<style scoped>
li{
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label {
float: left;
cursor: pointer;
}
li label li input {
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button{
float: right;
display: none;
margin-top: 3px;
}
li:before{
content: initial;
}
li:last-child{
border-bottom: none;
}
li:hover{
background-color: #ddd;
}
li:hover button{
display: block;
}
style>
MyList.vue文件:
<template>
<ul class="todo-main">
<Item1
v-for="todoObj in todoList"
:key="todoObj.id"
:itemPro="todoObj"
:checkTodoObj="checkTodoObj"
:deleteTodoObj="deleteTodoObj"
>Item1>
ul>
template>
<script>
import Item1 from './MyItem.vue'
export default {
name:"MyList",
components:{Item1},
props:['todoList','checkTodoObj','deleteTodoObj']
}
script>
<style scoped>
.todo-main{
margin-left: 0px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
.todo-empty{
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
border-radius: 2px;
padding-left: 5px;
margin-top: 10px;
}
style>
App.vue文件:
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<Header1 :receive="receive">Header1>
<List1
:todoList="todoList"
:checkTodoObj="checkTodoObj"
:deleteTodoObj="deleteTodoObj"
>List1>
<Footer1 :todoList="todoList" :checkAllTodoObj="checkAllTodoObj" :clearAllTodoObj="clearAllTodoObj">Footer1>
div>
div>
div>
template>
<script>
import Header1 from "./components/MyHeader.vue"
import Footer1 from "./components/MyFooter.vue"
import List1 from "./components/MyList.vue"
export default{
name:'App',
components:{
Header1,
List1,
Footer1,
},
data(){
return {
todoList:[
{id:'001',title:'吃饭',done:true},
{id:'002',title:'睡觉',done:false},
{id:'003',title:'敲代码',done:true},
]
}
},
methods:{
//从MyHeader中获取数据,添加todoObj对象
receive(todoObj){
//这样将值传给添加到todoList末尾中。
this.todoList.unshift(todoObj);
},
//勾选或者取消勾选一个todo
checkTodoObj(id){
this.todoList.forEach((todoObj)=>{
if(todoObj.id == id)
todoObj.done = !todoObj.done
})
},
//删除一个TodoObj
deleteTodoObj(id){
//注意:过滤出来的是一个新数组,并不是改变了data中的todoList。
//因此,要重新赋值一下。
// console.log(id)
this.todoList = this.todoList.filter((todoObj)=>{
return todoObj.id !== id
})
},
//全选or取消全选
checkAllTodoObj(done){
this.todoList.forEach((todo)=>{
todo.done = done
})
},
//清除所有已经完成的todoObj
clearAllTodoObj(){
this.todoList = this.todoList.filter((todo)=>{
return !todo.done
})
}
}
}
script>
<style>
body{
background-color: #fff;
}
.btn{
display: inline-block;
padding: 4px 12px;
margin-bottom: 0;
font-size: 14px;
line-height: 20px;
text-align: center;
vertical-align: middle;
cursor: pointer;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.2), 0 1px 2px rgba(0,0,0,0.05);
border-radius: 4px;
}
.btn-danger{
color: #fff;
background-color: #da4f49;
border: 1px solid #bd362f;
}
.btn-danger:hover{
color: #fff;
background-color: #BD362F;
}
.btn:focus{
outline: none;
}
.todo-container{
width:600px;
margin: 0 auto;
}
.todo-container .todo-wrap {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
style>