vuejs、vue CLI、webpack笔记
一、vue.js语法
1. 开始操作
1.1 安装vue.js
官网 下载js文件,添加到项目文件夹中
1.2 使用vue
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>title</title>
</head>
<body>
<div id='app'>{{message}}</div> <!-- 双括号mustache(胡须)语法 -->
<script src='../js/vue.js'></script>
<script>
//声明式编程,区别于document.getElementById('app')这种命令式编程
const app = new Vue({
el:'#app',//用于挂载要管理的元素
data:{//初始化数据
message:'vue 我来了'
}
})
</script>
</body>
</html>
2. 标签
2.1 v-if
作用:
v-if:'data',若data数值为真就渲染组件,否则不渲染。
2.2 v-else-if
作用:
与v-if结合使用,效果类似。
2.3 v-show
v-show='data',若data数值为真就显示组件,否则不显示,不显示效果等同于display:none;区别于v-if的不渲染。
2.4 v-pre
作用:
跳过这个元素和它的子元素的编译过程。一些静态的内容不需要编辑加这个指令可以加快编辑。
2.5 v-once
作用:
如果显示的信息后续不需要再修改,使用v-once,这样可以提高性能。
2.6 v-bind
作用:
动态将数据和属性(style,class...)标签进行绑定。
简写:':'
2.7 v-cloak
2.7.1 语法格式
<div v-cloak>{{data}}</div>
作用:
在网络延迟高时,可能会在页面突兀的显示代码: {{data}},加上v-cloak(斗篷)在data还没有完全加载出来前可以掩藏 {{data}}。
2.8 v-for
作用:
遍历数组或者对象元素,遍历出来的数据绑定组件逐个渲染。
示例
<!-- 根据实际开发中的重要性,先value,最后index;value,key,index名字可以自定义 -->
<div v-for='(value, key, index) in object'>
<div>{{index}} {{key}} {{value}} </div>
</div>
2.9 v-on
作用
绑定事件函数,示例:v-on:click = 'clickFunction' 等同于 on-click = 'clickFunction'
修饰符
- stop:停止向上冒泡
- once:使绑定的事件只响应一次
- prevent:阻止组件的默认行为,比如form表格的默认active行为
- {keyCode|keyAlias}:对按键事件的过滤,例如:v-on:key.enter = 'functionName' 只有按下enter建才进行回调
- native:监听组件根元素的原生事件
简写:'@'
2.10 v-model
作用:
实现数据与表单的双向绑定。
原理
<input v-model="message" value="message">
<!-- 上面代码效果等同于下面-->
<input v-bind:value="message" @input="message=$event.target.value"/>
v-model 结合单选按钮
<body>
<div id='app'>
<input value='男' type='radio' name='radio-group' v-model="sex"/>男
<input value='女' type="radio" name='radio-group' v-model='sex'/>女
<h3>{{sex}}</h3>
</div>
<script src='../js/vue.js'></script>
<script>
const app = new Vue({
el:'#app',
data:{
sex:'男'
}
})
</script>
</body>
v-model 结合复选框
复选框单选对应 bool值,多选对应数组[]
<body>
<div id='app'>
<input type="checkbox" v-bind:value='agree' v-model='agree' />同意协议
<h3>{{agree}}</h3>
</div>
<script src='../js/vue.js'></script>
<script>
const app = new Vue({
el:'#app',
data:{
agree:false
}
})
</script>
</body>
<body>
<div id='app'>
<div v-for='(hobby,index) in hobbies'>
<input v-bind:value='hobby' type='checkbox' name='index' v-model="selectedHobbies"/>{{hobby}}
</div>
<h3>{{selectedHobbies}}</h3>
</div>
<script src='../js/vue.js'></script>
<script>
const app = new Vue({
el:'#app',
data:{
hobbies:['游泳','跑步','看书','绘画'],
selectedHobbies:[]
}
})
</script>
</body>
v-model 结合select
<!-- 若想实现多选 添加标签:multiple -->
<select name='abc' v-model='fruit'>
<option value='苹果'>苹果</option>
<option value='香蕉'>香蕉</option>
<option value='葡萄'>葡萄</option>
<option value='榴莲'>榴莲</option>
</select>
修饰符:
- lazy:对于绑定的组件,只在其失去焦点或按下回车才更新vue实例中的数据。
- number:传入vue实例中的数据为Number对象
- trim:除去数据中的前后空格
3. 组件中的key属性
3.1 作用:
唯一确定组件,使组件能不随意更新。通俗讲可以让组件更好复用或不复用。
3.2 示例:若不加入key,js内核会复用input组件,造成组件中的值会保留。
<body>
<div id='app'>
<div v-if='flag'>
<input key='account'/>账号登录
</div>
<div v-else>
<input key='email'/>邮箱登录
</div>
<button @click="btnClick">{{login}}</button>
</div>
<script src='../js/vue.js'></script>
<script>
const app = new Vue({
el:'#app',
data:{
flag:true,
login:'切换邮箱登录'
},
methods:{
btnClick(){
if(this.flag){
this.flag = !this.flag;
this.login = '切换账号登录';
}else{
this.flag = !this.flag;
this.login = '切换邮箱登录'
}
}
}
})
</script>
</body>
4. 响应式函数
4.1 响应式操作
修改vue实例中数据,绑定vue实例中数据的组件/属性会实时更新。通过函数实现的响应式操作即为响应式函数。
4.2 关于数组操作的响应式函数
- push('') 最后加多元素
- pop() 删最后一个
- shift() 删第一个
- unshift('') 最开头添加多个元素
- splice('','','') start, length ,item 它可以进行删除元素,插入元素,替换元素
- reverse 数组反转
4.3 非响应式操作
通过索引改变数组!!!
5. Vue实例中的过滤器对象
5.1 作用
将数据按照一定的规则转化为另外一个数据返回。
5.2 示例:将price作为参数传入priceFilter中,返回修改后的price
<body>
<div id='app'>
<!-- 使用过滤器 ,默认将price作为参数传入给priceFilter-->
<h4>价格:{{price | priceFilter}}</h4>
</div>
<script src='../js/vue.js'></script>
<script>
const app = new Vue({
el:'#app',
data:{
price:12.3
},
filters:{ //在vue实例中加入filter对象,在filter对象里面定义过滤器(函数)
priceFilter(price){
//保留小数2位:Number.toFixed(2)
return '¥' + price
}
}
})
</script>
</body>
6. js中高阶函数 (很多高级语言中都会有)
6.1 filter()
result = array.filter(callback(n){}) 回调函数返回的必须为一个bool值,若为true自动 将n加入内部数组,否则不加入
6.2 map()
result = array.map(callback(n){}) 回调函数的返回值为新数值返回
6.3 reduce()
result = array.reduce(callback(n1,n2){},0) 第二个参数为第一个值的初始值, 对两个值进行操作,返回操作后的结果作为下一组传入的第一个值。
7. 组件化
7.1 自定义第一个组件 (创建构造器,注册组件,使用组件)
<body>
<div id='app'>
<!-- 使用组件 -->
<my-component></my-component>
</div>
<script src='../js/vue.js'></script>
<script>
//创建组件构造器
creater = Vue.extend({
//tab键上面的那个符号在ES6中可以实现内部内容任意换行圈成一个整体
name:'cmp',
template:`
<div>
<h4>我的第一个组件</h4>
</div>
`
})
//注册组件 (此方法注册的组件为全局组件,所有vue实例挂载的元素中都可以使用)
Vue.component('my-component',creater)
const app = new Vue({
el:'#app'
})
</script>
</body>
7.2 全局组件注册的简写(语法糖)
//Vue.compontent会自动将第二个参数{}传给Vue.extend()
Vue.component('my-component',{
template:`<div></div>`
})
7.3 局部组件注册 (确立一个概念:Vue实例本身是个组件)
<body>
<div id='app'>
<cpn></cpn>
</div>
<div id='app2'>
<!-- <cpn></cpn> 试图引用app实例的局部组件,报错! -->
</div>
<script src='../js/vue.js'></script>
<script>
const cpnC = Vue.extend({
template:'<div>局部组件</div>'
})
const app = new Vue({
el:'#app',
components:{
cpn:cpnC
}
})
const app2 = new Vue({
el:'#app2'
})
</script>
</body>
7.4 组件中的模板(template)分离写法
1.通过脚本分离:
<script type='text/x-template' id='temp'>
<div>
在类型为'text/x-template'的script中定义
</div>
</script>
<script >
//通过id引用
Vue.component('my-component',{template:'#temp'})
</script>
2.通过组件模板分离
<template id='#temp'>
<div>
在template组件中定义模板
</div>
</template>
<script>
Vue.component('my-component',{template:'#temp'})
</script>
7.5 向组件中存放数据
<!-- 使用组件中message数据 -->
<cpn>{{message}}</cpn>
--------------------------------------------------
<script>
Vue.component('cpn',
{
template:'#cpn',
data() //这个对象必须为函数
{
return {message:'abc'}
}
}
)
</scirpt>
7.6 思考:组件中data为什么是函数?
这可以保证数据作用域仅限于当前组件,而不会作用于其他相同的组件!
7.8 创建父子组件
<body>
<div id='app'>
<!-- 使用组件 -->
<cpn2></cpn2>
</div>
<script src='../js/vue.js'></script>
<script>
const cpnC = Vue.extend({
template:'<div>子组件</div>'
})
Vue.component('cpn2',{
template:'<div>父组件<cpn></cpn></div>',
components:{
cpn : cpnC //*这里只能是一个组件构造器,若为一个组件就报错找不到
}
}
)
const app = new Vue({
el:'#app'
})
</script>
</body>
7.9 父子组件的通信
需求:子组件不能引用父组件或者Vue实例的数据,但是实际开发中却需要父子组件相互通信!
Vue提供的方法:
通过props(properts 简写)向子组件传递数据;
通过事件($emit Events)向父组件发送数据。
1. 父组件向子组件传递数据:
<body>
<div id='app'>
<!-- 父组件向子组件传递数据 -->
<cpn v-bind:movies='movieList'></cpn>
</div>
<template id="temp">
<div>
<ul>
<!-- 遍历显示组件中的movies数据 -->
<li v-for='movie in movies'>{{movie}}</li>
</ul>
</div>
</template>
<script src='../js/vue.js'></script>
<script>
const app = new Vue({
el:'#app',
data:{
movieList:['三傻大闹宝莱坞','我不是药神','唐人街探案']
},
components:{
cpn:{
template:'#temp',
props:['movies'] //这里的movies在上面被当成一个变量属性
}
}
})
</script>
</body>
1.1 props的一些可选操作
-
数据类型限制:
props: -
带默认值:(对象中 required 表示 是否必须传数据)
props:
{movies:
{
type:Array,
default:[''],
required:true
}
}
上面的语法有个错误,default默认值为数组或对象类型时default必须为一个函数:default(){return []}
- 自定义数据类型:
props:
1.2 props中数据变量驼峰命名问题:
props:{moviesName:Array} //声明时为驼峰
2.子组件向父组件传递数据:($emit发射的事件同样不能用驼峰命名法)
<!-- 触发流程:btnClick -->cpn-event -->getCpnData -->
<body>
<div id='app'>
<cpn @cpn-event='getCpnData'></cpn>
<h2>子组件传来的数据:{{cpnData}}</h2>
</div>
<template id="temp">
</template>
<script src='../js/vue.js'></script>
<script>
const app = new Vue({
el:'#app',
data:{
cpnData:''
},
methods:{
getCpnData(item){
this.cpnData = item
}
},
components:{
cpn:{
template:`
<div>
<button @click="btnClicked">子组件按钮</button>
</div>
`,
data(){
return {item:'子组件数据'}
},
methods:{
btnClicked(){
return this.$emit('cpn-event',this.item)
}
}
}
}
})
</script>
</body>
扩展知识
<!-- component watch 对象 监听数据改变;
watch中的函数名 和 data 返回的变量名相同 -->
data(){
return {number1:123}
},
watch:{
number1(newValue, oldValue){
this.number1 = newValue;
this.$emit('numb1change',this.number1)
}
}
7.10 父子组件互相访问
1.父组件访问子组件
$children 或者 $refs (reference)
this.$children[0].function()
this.$children[1].name
在实际开发中一般不使用this.$children ,组件数量随时会变,下标取值不稳定
this.$refs.aaa 父组件中访问子组件,refs默认为空,在组件中需要加ref
<cpn ref='aaa'> </cpn> 组件中加入ref使用
2.子组件访问父组件
his.$parent //在子组件中访问父组件
this.$root //在子组件中访问vue实例
7.11 slot 插槽
作用
使组件内部具有更好可扩展性。使得我们可以给自定义组件中添加中添加其他html内容。
使用方法
<!-- 1.在模板中定义slot -->
<template id="temp">
<div>自定义组件
<slot></slot>
</div>
</template>
<!-- 2.使用带有slot的模板的组件 -->
<!-- h2 和 button 会替换组件中的slot,可以将slot理解为一个占位符 -->
<cpn>
<h2>给自定义组件添加html元素</h2>
<button>按钮</button>
</cpn>
slot常见几种情形
<!-- 1.带默认值slot -->
<!-- 若不给自定义组件中添加html内容,默认显示button;若添加了就替换button -->
<slot><button>按钮</button></slot>
<!-- 2.具名(带name)slot -->
<template id='barTemp'>
<slot name='left'></slot>
<slot name='center'></slot>
<slot name='right'></slot>
</template>
<!-- 使用:(指名道姓替换组件中某个插槽) -->
<cpn><button slot='left'>返回</button></cpn>
<cpn><span slot='center'>标题</span></cpn>
<cpn><button slot='right'>搜索</button></cpn>
<!-- 3.作用域slot :父组件替换插槽的标签,但是内容由子组件来提供-->
<!-- 需求:内容在子组件,希望父组件告诉我们如何展示,怎么办? -->
<template>
<slot v-bind:data='子组件数据'></slot>
</template>
<!-- 重点是:slot-scope='slot' -->
<cpn>
<template slot-scope='slot'>
<div v-for='item in slot.data'>{{item}}</div>
</template>
</cpn>
二、模块化和webpack
1. 模块化规范
1.1 常见模块化规范:
CommonJS(node采用的该规范)、AMD、CMD、ES6的Modules
1.2 CommonJS 的导出
请在node 环境下使用
a.js文件:
flag = true
sum(a,b){
return a+b
}
<!-- 重点:module.exports -->
module.exports = {
flag:flag,
sum:sum
}
2.2 CommonJS 的导入
b.js文件
<!-- 重点:require -->
var obj = require('a.js')
console.log(obj.flag)
console.log(obj.sum(1,2))
2.3 脚本标签中的 module 类型
作用:
将每个js文件作为独立的模块,避免了js文件间命名冲突。
演示
a.js文件中:
var flag = true
if(flag){console.log('a.js文件')}
b.js文件中:
if(flag){console.log('b.js文件')}
index.html 文件中
<!-- 加入type='module' 后会报错 ,
因为type='module' 可以保证多个文件共同引用不会出现命名冲突-->
<script src='a.js' type='module'></script>
<script src='b.js' type='module'></script>
思考:一个js文件想引用另外一个js文件中的数据ES6中怎么实现?
ES6 标准中的export,import 的运用。
2.4 ES6 的导出
a.js文件中 export演示
var name = '鲤鱼的探险历程'
getName()
{
return name
}
//方式一:对象形式导出
export {name, getName}
//方式二:直接导出
export var name2 = 'vue coder'
//方式三:匿名导出
const email = '123@126.com'
export default email //同一个模块(文件)中只能存在一个
2.5 ES6 的导入
b.js文件中 import演示
//对应方式一:
import {name, getName} from 'a.js'
//对应方式二:
import {name2} from 'a.js'
//对应方式三:
import my_email from 'a.js' //导入者自己命名变量
应用于除了匿名导出的其他方式:
//‘* as ’的作用:给导入对象命名,避免 1.导入变量和当前文档中的变量相同产生冲突,2.导入数据过多的问题
import * as aaa from '../aa.js'
console.log(aaa.val)
2. webpack的使用
webpack官网
webpack中文
2.1 webpack 的安装
全局安装
npm install webpack -g
本地(局部)安装
npm install webpack --save
2.2 建立项目
目录结构
- dist //保存打包后的文件
- src //源代码文件夹
* main.js //在该文件引用所有js文件
* a.js
* b.js - index.html //项目入口文件
2.3 打包文件
把终端(cmd)路径换成当前项目路径,然后键入(4.x.x版本):
webpack ./src/main.js -o ./dist/bundle.js
webpack3.x.x的版本使用:
webpack ./src/main.js ./dist/bundle.js
解释:将main.js以及它所引用的所有js文件打包到bundle.js文件;
若是webpack 5.x.x会在dist/bundle.js 的文件夹中生成打包后的main.js
思考:怎么精简:webpack ./src/main.js ./dist/bundle.js ?
需要对webpack进行配置。
配置流程:
- 当前项目中建立文件:webpack.config.js
- webpack.config.js中添加内容:
const path = require('path') //导入node的path包
module.exports = {
entry:'./src/main.js',
//output中的路径必须为绝对路径
output:{
//__dirname 为node中上下文全局变量,值为当前路径,
//path.resolve 进行路径拼接
path:path.resolve(__dirname,'dist'),
filename:'bundle.js'
},
mode:'development' //不加这条代码,在4.x.x以上的webpack默认打包为压缩后的代码
}
//概括:将src下的main.js文件打包到当前路径下的dist文件夹的bundle.js文件中
思考:这里引用了nodo的path包,那么就需要配置一个package.json文件,该文件如何配置?
1. 在当前项目路径下运行终端命令:npm init
2. 按照提示进行配置,配置完后项目中就会生成 package.json 文件
<!-- package.json 文件内容 -->
{
"name": "webpacktest",
"version": "1.0.0",
"description": "",
"main": "index.js",//随便填的入口文件
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
- 以后在终端直接输入:webpack ,即可起到相同打包效果。
2.4 npm 运行 webpack
思考:若配置文件很多webpack命令就会又变得很长,怎么解决?
用 npm run ... 代替。
怎么用npm run build 代替 webpack ?
在 package.json 的scripts中添加:"build":"webpack"
运行npm run build 时,会默认先从局部(本地)找webpack,再找全局
这里面存在一个问题:若项目中需要低版本的webpack怎么办?
安装局部(本地)webpack : npm install webpack@3.6.0 --save-dev //有-dev意思为开发时依赖
在终端使用webpack命令会使用全局的webpack. 想访问本地需要输入 ./node_module/.bin/webpack
2.5 webpack.loader
loader作用
将 less-->css ,TypeScript --> js ...
总的一句话:将其他文件打包为css、js、html这种浏览器认识的基本类型
loader的使用
步骤一:npm 安转需要使用的loader;(不同文件打包,对应不同loader)
步骤二:在webpack.config.js 中的module关键字下进行配置
2.5.1 打包CSS
1.npm install --save--dev css-lodeer style-loader
2.webpack.config.js 文件配置:
module:{
rules:[
{
test:/\.css$/,
use:['style-loader','css-loader']
//css-loader 负责加载css文件,style-loader负责编译
//为什么css-loader写在后面,因为node数据加载默认从右至左
}
]
}
2.5.2 打包less
1.npm install --save-dev less-loader less //less负责转换less-->css
2.{test:/.less$/, use['style-loader','css-loader','less-loader']}
2.5.3 打包图片
1.npm install --save-dev url-loader
2.webpack.config.js文件中:
module:{
rules:[
{
test:/\.(png|jpg|jpeg)$/,
use:[
{
loader:'url-loader',
options:{limit:8192}
//小于limit大小的图片会被base64编码后替换图片url
//当加载大于limit的图片时需要file-loader 模块
}
]
}
]
}
3.若image的大小有大于limit限制的才有这步骤:
npm install --save-dev file-loader
!!!若运行程序入口网页index.html不在dist包中,可能会报路径加载错误
原因:file-loader会将照片文件加载到打包文件中,这样源代码中的路径就会有问题。
解决方法:使打包照片路径时候自动添加上dist路径,在webpack.config.js的output中加上:publicPath:'dist/'
file-loader会用hash值命名图片,若需求希望打包后的文件有一定的规范:
希望放在img文件夹中,原来文件名+hash的前8位+文件类型
(为什么不直接用文件名字,在实际开发中项目中可能存在相同名字的图片被同时加载到img文件夹)
操作:options中加上:name:'img/[name].[hash:8].[ext]'
2.5.4 ES6代码转为ES5代码
1.npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
2.{test:/.js$/, exclude:/(node_modules|bower_components)/,use:{loader:'babel-loader',options:{presets:['es2015']}}}
2.6 配置vue环境
1.npm install --save vue //安装运行时依赖,非开发时依赖!
2.导入vue: import Vue from 'vue'
再运行 :npm run build 可能会报错
原因:在使用vue的文件中存在 const app = new Vue({}),会被当作编译template,而runtime-only 版本不支持template
解决方法:使用 runtime-compiler 的vue版本。
操作:在webpack.config.js中配置中加入 resolve:{alias:{'vue$':'vue/dist/vue.esm.js'}}
原理:将import Vue from 'vue'中的vue引入了另一个文件,vue.esm.js相当于使用runtime-compile
打包.vue文件
1.npm install vue-loader vue-template-compiler --save-dev
2.{test:/.vue$/,user:['vue-loader']}
打包时可能报错:vue-loader 缺少一个插件,14版本上的webpack需要配置一个插件
解决方法:在package.json中修改 'vue-loader':'^13.0.0' 版本限制在13-14间,再执行 npm install
怎么省略输入文件类型?
在webpack.config.js的module.export中加入:
resolve:{//用来解决路径问题
"extensions":['.js','.css','.vue']
}
2.7 plugin 插件的使用
步骤一:通过npm安转需要应用的插件
步骤二:在webpack.config.js中的plugins中配置插件
loader和plugin区别:loader像一个加载器、转换器;plugin是一个扩展器。
plugin常用的有:给js添加一个版权声明,代码压缩(丑化)
·版权声明操作:
在webpack.config.js文件中
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry:'./src/main.js',
output:{
//__dirname 为node中上下文全局变量,值为当前路径,
//path.resolve 进行路径拼接
path:path.resolve(__dirname,'dist'),
filename:'bundle.js'
},
mode:'development',
resolve:{//用来解决路径问题
"extensions":['.js','.css','.vue']
},
plugins:[
new webpack.BannerPlugin('我的版权')
]
}
·打包html的plugin
真实发布项目时,发布的dist文件夹中的内容需要index.html文件
执行:npm install html-webpack-plugin --save-dev
const HtmlWebpackPlugin = require(html-webpack-plugin)
--------------------------------------------------
plugins:[
new HtmlWebpackPlugin(
{template:'index.html'})//根据模板生成index.html
]
打包成功后会在dist文件夹下生成index.html文件
文件中自动加入


浙公网安备 33010602011771号