vue初学习
视频:黑马程序员vue教程
课程资料:https://pan.baidu.com/s/17qA1j5kyEpmg80aW3bUwYg?pwd=1234
内容:
前端工程化
小白眼中的前端开发:
会写HTML + CSS + JavaScript
就会前端开发
需要美化页面样式,就拽一个bootstrap
过来
需要操作DOM或发起Ajax请求,再拽一个jQuery
过来
需要快速实现网页布局效果,就拽一个Layui
过来
实际的前端开发:
模块化(js的模块化、Css的模块化、资源的模块化)
组件化(复用现有的Ul结构、样式、行为)
规范化(目录结构的划分、编码规范化、接口规范化、文档规范化、Git 分支管理)
自动化(自动化构建、自动部署、自动化测试)
前端工程化:在企业级的前端项目开发中,把前端开发所需的工具、技术、流程、经验等进行规范化,标准化
企业中的Vue项目和React项目,都是基于工程化的方式进行开发的。
好处:前端开发自成体系,有一套标准的开发方案和流程。
前端工程化解决方案:
早期:
grunt:https://www.gruntjs.net/
gulp:https://www.gulpjs.com.cn/
主流:
webpack:https://www.webpackjs.com/
parcel:https://zh.parceljs.org/
webpack
概念:webpack是前端项目工程化的具体解决方案
主要功能:
本质上,webpack 是一个现代 JavaScript 应用程序的*静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph)*,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle
它提供了友好的前端模块化开发支持,以及代码压缩混淆、处理浏览器端 JavaScript 的兼容性、性能优化等强大的功能
简单地说,就是可以把多个js文件打包成一个文件
webpack的基本使用
创建隔行变色项目
- 新建项目空白目录,并运行
npm init -y
命令,初始化包管理配置文件package.json
- 新建src源代码目录
- 新建
src -> inde.html
首页和src -> index.js
脚本文件 - 初始化首页基本的结构
- 运行
npm install jquery -S
命令,安装jQuery
- 通过
ES6
模块化的方式导入jQuery
,实现列表隔行变色效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="./index.js"></script>
<title>Document</title>
</head>
<body>
<ul>
<li>这是第 1 个li</li>
<li>这是第 2 个li</li>
<li>这是第 3 个li</li>
<li>这是第 4 个li</li>
<li>这是第 5 个li</li>
<li>这是第 6 个li</li>
<li>这是第 7 个li</li>
<li>这是第 8 个li</li>
<li>这是第 9 个li</li>
</ul>
</body>
</html>
// 使用es6导入jquery
import $ from "jquery"
//定义入口
$(function(){
$('li:odd').css('background-color','red')
$('li:even').css('background-color','pink')
})
注意:这里odd
是偶数(第0行是偶数),even
是基数
由于浏览器不识别es6语言报错
安装并使用webpack解决问题
npm install webpack@5.42.1 webpack-cli@4.7.2 -D
创建webpack.config.js:
// 使用Node.js中的导出语法,向外导出一个webpack的配置对象
module.exports = {
mode:'development' // 代表webpacck运行的模式,可选值有development和production
}
在package.json里添加:
"scripts": {
"dev": "webpack"
},
运行脚本:
npm run dev
注意:运行dev里的webpack时,会先读取webpack.config.js里的配置文件
让html调用dist里的main.js:
<script src="../dist/main.js"></script>
注意:这里的main.js
文件的实质就是把index.js
和jquery
打包成一个文件后的结果
此时可以正常调用:
注意:由于设置的是development模式,打包后的main.js
不会压缩代码
更改wabpack.config.js
里的mode
为production
模式即可压缩代码:
module.exports = {
mode:'production'
}
注意:
使用production模式会进行压缩混淆,缩小文件体积,但是延长了打包时间,因此上线时用production,开发时用development
在webpack 4.x 和 5.x 的版本中有如下的默认约定:
默认的打包入口文件为src -> index.js
,默认输出文件路径为dist -> main.js
自定义打包的入口与出口
简介:在webpack.config.js
配置文件中,通过entry
属性指定打包的入口,通过output
属性指定打包的出口。
示例:
// 导入path文件
const path = require('path')
// 使用Node.js中的导出语法,向外导出一个webpack的配置对象
module.exports = {
mode:'development', // 代表webpacck运行的模式,可选值有development和production
// 指定打包文件
entry: path.join(__dirname,'./src/index.js'),
// 指定生成的文件要存放的位置
output :{
// 指定存放的目录
path: path.join(__dirname,'dist'),
// 指定生成的文件名
filename: 'bundle.js'
}
}
问题:每次都要重新打包才能看到效果
webpack插件
webpack-dev-server
简介:该插件可以在源代码修改后自动进行打包操作
安装:
npm install webpack-dev-server@3.11.2 -D
调用webpack server:
更改package.json
里的script
:
"scripts": {
"dev": "webpack serve"
},
出现报错:
Unable to load '@webpack-cli/serve' command
把webpack-cli升级到4.10.0以上解决问题:
npm i webpack-cli@4.10.0 -D
重新运行:
npm run dev
打开网址:http://localhost:8080/src/
发现样式和文件并没有变化,因为最新的文件放在了内存里,而index.html里调用的还是../dist/bundle.js:
更改html里的路径来匹配内存文件:
<script src="/bundle.js"></script>
注意:这里的/
(根)指的是项目的根
html-webpack-plugin
简介:可以直接展示html页面
安装:
npm install html-webpack-plugin@5.3.2 -D
修改webpack.config.js配置:
// 导入path文件
const path = require('path')
// 导入 html-webpack-plugin 这个插件,得到插件的构造函数
const HtmlPlugin = require('html-webpack-plugin')
// new 构造函数,创建插件的实例对象
const htmlPlugin = new HtmlPlugin({
// 指定要复制哪个页面
template: './src/index.html',
// 指定复制出来的文件名和存放路径
filename: './index.html'
})
// 使用Node.js中的导出语法,向外导出一个webpack的配置对象
module.exports = {
mode:'development', // 代表webpacck运行的模式,可选值有development和production
// 指定打包文件
entry: path.join(__dirname,'./src/index.js'),
// 指定生成的文件要存放的位置
output :{
// 指定存放的目录
path: path.join(__dirname,'dist'),
// 指定生成的文件名
filename: 'bundle.js'
},
// 插件的数组,webpack在运行时会加载并调用这些插件
plugins: [htmlPlugin]
}
配置完成后,重启服务即可直接动态刷新显示页面
配置devServer
介绍:devserver可以让服务启动后自动打开页面
配置:在webpack.config.js添加如下配置
// 首次打包成功后自动用默认浏览器打开
devServer: {
open: true,
// 自定义端口80
port: 80
}
Loader
简介:在实际开发过程中, webpack
默认只能打包处理以.js
后缀名结尾的模块。其他非.js
后缀名结尾的模块, webpack
默认处理不了,需要调用loader加载器才可以正常打包,否则会报错
css-loader
处理css:创建一个新的css
样式文件,在index.js
里导入样式
li{
list-style: none;
}
// 使用es6导入jquery
import $ from "jquery"
// 导入样式
import './css/index.css'
//定义入口
$(function(){
$('li:odd').css('background-color','red')
$('li:even').css('background-color','cyan')
})
安装css-loader:
npm i style-loader@3.0.0 css-loader@5.2.6 -D
在webpack配置项里新增module配置:
module:{
rules: [
// 定义不同模块对应的loader
{ test:/\.css$/, use: ['style-loader', 'css-loader'] }
]
}
重新运行dev脚本可发现css样式生效!!!
注意:
webpack
默认只能打包处理.js
结尾的文件,处理不了其它后缀的文件由于代码中包含了
index.css
这个文件,因此webpack
默认处理不了当
webpack
发现某个文件处理不了的时候,会查找webpack.config.js
这个配置文件,看module.rules
数组中,是否配置了对应的loader加载器webpack
把index.css
这个文件先转交给最后一个loader进行处理(先转交给css-loader
)。当
css-loader
处理完毕之后,会把处理的结果,转交给下一个loader
(转交给style-loader
)当
style-loader
处理完毕之后,发现没有下一个loader
了,于是就把处理的结果,转交给了webpack
webpack
把style-loader
处理的结果,合井到/dist/bundle.js
中,最终生成打包好的文件
less-loader
什么是less:Less (Leaner Style Sheets 的缩写) 是一门向后兼容的 CSS 扩展语言
中文教程:https://less.bootcss.com/
创建less文件:
html,
body,
ul {
margin: 0;
padding: 0;
li {
line-height: 30px;
padding-left: 20px;
font-size: 12px;
}
}
添加less-loader:
npm i less-loader@10.0.1 less@4.1.1 -D
在webpack的配置文件添加规则:
module:{
rules: [
// 定义不同模块对应的loader
{ test:/\.css$/, use: ['style-loader', 'css-loader'] },
// 处理.less文件的loader
{ test:/\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
]
}
在index.js引入less.js文件:
import './css/index.less'
重新运行dev脚本可发现less样式生效!!!
url-loader
关于base64图片:网页中可以直接调用图片的base64码防止过多请求
<img src="base64码" alt="">
注意:转码以后文件变大,因此不建议把大图片转换成base64
在网页中添加图片:
在index.html添加图片标签:
<img src="" alt="" class="box">
在index.js引入图片:
// 导入图片
import logo from './image/logo.jpg'
// 给img标签的src动态赋值
$('.box').attr('src',logo)
安装:
npm i url-loader@4.1.1 file-loader@6.2.0 -D
在webpack配置文件添加规则:
module:{
rules: [
// 定义不同模块对应的loader
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
// 处理.less文件的loader
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
// 处理图片文件的loader
// 如果需要调用的loader只有一个,则只传递一个字符串也行,否则需要指定数组
{ test: /\.jpg|.png|.gif$/, use: 'url-loader'}
]
}
}
重新运行dev脚本
注意:这里图片被转换成了base64的格式
注意:
在webpack里,一切皆模块,都转化成js运行
如果某个模块中,使用from接收到的成员为undefined, 则没必要进行接收。一般
css
样式不需要接收,只需要加载。这种导入最终会生成一个bundle.js
,这个bundle.js
包含转化后的css
样式
添加参数指定图片转换临界值:
{ test: /\.jpg|.png|.gif$/, use: 'url-loader?limit=1024' }
这里表示大于1024(1kb)后不会转换成base64图片,小于或等于时会被转换
babel-loader
简介:webpack只能打包处理一部分高级的 JavaScript语法。对于那些webpack无法处理的高级js语法,需要借助于babel-loader进行打包处理。
安装:
npm i babel-loader@8.2.2 @babel/core@7.14.6 @babel/plugin-proposal-decorators@7.14.5 -D
注意:babel/plugin-proposal-decorators
是插件的插件
使用:在项目的根目录下创建名为babel.config.js
的配置文件,定义Babel的配置项如下:
module.exports = {
//声明 babel 可用插件
plugins: [["@babel/plugin-proposal-decorators", {legacy: true}]]
}
在webpack配置项新增:
{ test:/\.js$/, use: 'babel-loader', exclude: /node_modules/ }
重新运行后可以看到控制台输出的:Person info.
配置打包命令
简介:项目完成后需要打包成html,css,js 三大件给后端发布。
添加命令
这时需在package.json
里添加一条nulid
命令:
"scripts": {
"dev": "webpack serve",
"build": "webpack --mode production"
},
注意:这里修改了打包模式为压缩模式,--mode production
的优先级比配置文件高,会优先执行。平时使用的是开发模式:
安装clean-webpack-plugin
简介:每次生成文件时,旧文件会有残留,需要手动删除很麻烦。安装clean-webpack-plugin
来在每次生成时自动清除旧文件
npm文档:https://www.npmjs.com/package/clean-webpack-plugin
安装:
npm install --save-dev clean-webpack-plugin
添加插件:
在webpack.config.js
中的plugin导入插件:
new CleanWebpackPlugin()
将js打包放进文件夹
在webpack.js修改output内容如下:
filename: 'js/bundle.js'
将图片放进文件夹
在webpack.js修改module内容如下:
{ test: /\.jpg|.png|.gif$/, use: 'url-loader?limit=1024&outputPath=images' },
在cmd运行即可:
npm run bulid
配置Source Map
简介:Source Map
是一个信息文件,里面存储着压缩混淆前后代码所对应的位置信息。开发环境下默认生成的Source Map
记录的是混淆后代码的位置。
在webpack.config.js
中的module.exports
添加如下配置:
devtool: 'eval-source-map', //开发环境
重启即可:
npm run dev
注意:在生产环境下,如果webpack配置文件中注释了devtool选项,则最终生成的文件中不包含source map,这能够防止源代码通过source map的形式暴露
生产环境下:将devtool内容改为下列值,或者直接关闭
devtool: 'nosources-source-map', //只定位,不显示源码
生产环境下显示报错源码:不建议
devtool: 'source-map',
JS框架
现在流行的JS框架:ANGULAR
,REACT
,VUE
历史:多亏了Javascript
,在过去十年里,我们的网页变得更强大与动态化。我们已经把很多传统的服务端代码放到了浏览器中,这样就产生了成千上万行的Javascript
代码。
它们连接了各式各样的HTML
和CSS
文件,但是他们缺乏正规的组织形式。这也是为什么越来越多的开发者使用Javascript
框架的原因。
js编写的类型:声明式,命令式
Vue.js
英文官网:https://vuejs.org/
作者:尤雨溪(一位华裔前Google工程师)
作用:动态构建用户界面(vue是响应式的)
介绍
vue的特点
- 遵循MVVM模式
- 编码简洁,体积小,运行效率高,适合移动/PC端开发
- 它本身只关注UI,可以轻松引入vue插件(依赖vue)或其他第三方库(不依赖vue)开发项目
与其他前端JS框架的关联
- 借鉴
angular
的模板和数据绑定技术 - 借用
react
的组件化和虚拟化DOM技术
vue扩展插件
vue-cli:vue脚手架
**vue-resource(axios)**:ajax请求
vue-router:路由
vuex:状态管理
vue-lazyload:图片懒加载
vue-scroller:页面滑动相关
mint-ui:基于vue的UI组件库(移动端)
element-ui:基于vue的UI组件库(PC端)
vue.js下载:https://cn.vuejs.org/v2/guide/installation.html#Vue-Devtools
MVVM思想
介绍:前端的视图层,被mvvm思想划成了三部分,也就是每个页面都被分成了三部分
M(Model) :模型,数据对象(data)
V(View) :视图,模板页面(DOM)
**VM(ViewModel)**: 视图模型(Vue实例)。是调度者,用来联系M层和V层,提供DOM监听和数据绑定功能
MVVM相比于MVC提供了数据的双向绑定机制
一个hello world
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望Vue控制div -->
<div id="app">{{ username }}</div>
<!-- 导入Vue -->
<script src="./vue.js"></script>
<script>
// 创建vue实例对象
const vm = new Vue({
// el 指定选择器
el: '#app',
// data 对象是要渲染的数据
data: {
username: 'zhangsan'
}
})
</script>
</body>
</html>
vue的指令与过滤器
简介:指令(Directives) 是vue为开发者提供的模板语法,用于辅助开发者渲染页面的基本结构。
注意:使用指令之前一定要用el控制范围
内容渲染指令
简介:内容渲染指令用来辅助开发者渲染DOM元素的文本内容
注意:
- v-test和
{{ }}
只能渲染纯文本,要渲染html标签需要v-html - 插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!
v-text
简介:将v-text指定的内容覆盖显示
注意:在标签被控制的情况下
<div id="app">
<p v-text="username"></p>
</div>
插值表达式语法
简介:显示双括号里指定的内容,开发用的最多
<div id="app">
<p>姓名:{{username}}</p>
<p>性别:{{gender}}</p>
</div>
插值表达式还可以使用js语法:
<div>{{ tips }} 反转的结果是:{{tips.split('').reverse().join('')}}</div>
v-html
简介:覆盖渲染标签文本
在data里添加:
info: '<h4 style="color:red; font-weight:bold">欢迎大家学习vue</h4>'
在html数据:
<div v-html="info"></div>
属性绑定指令
简介:给html标签属性动态绑定值
v-bind
data数据:
tips: '请输入用户名',
html数据:
<div id="app">
<input type="text" v-bind:placeholder="tips">
<hr>
<img v-bind:src="photo" alt="" width="150px">
</div>
这里的v-bind
可以简写成:
<div id="app">
<input type="text" :placeholder="tips">
<hr>
<img :src="photo" alt="" width="150px">
</div>
v-bind可以进行动态拼接:
<div :title="'box' + index">这是一个div</div>
事件绑定指令
v-on
简介:vue提供了v-on事件绑定指令,用来辅助程序员为DOM元素绑定事件监听
注意:
原生DOM对象有:onclick
, oninput
, onkeyup
等
对应vue事件绑定形式为:v-on:click
, v-on:input
, v-on: keyup
对应简写为:@click
, @input
, @keyup
<div id="app">
<p>count的值是:{{count}}</p>
<button v-on:click="add">+1</button>
<button v-on:click="sub">-1</button>
</div>
<script>
// 创建vue实例对象
const vm = new Vue({
// el 指定选择器
el: '#app',
// data 对象是要渲染的数据
data: {
count: 0,
},
// methods的作用: 定义事件的处理函数
methods: {
add: function () { //可以简写为add ()
// console.log(vm === this)这里可以证实vm和this是相等的
// vm.count += 1
this.count += 1
},
sub() {
this.count -= 1
}
}
})
</script>
这里的函数需要传参时:
html数据:
<button v-on:click="add(2)">+1</button>
<!-- 这里的2可以作为参数传入下面funtion(n)函数 -->
<button v-on:click="sub">-1</button>
这里的v-on可以简写:
<button @click="add(2)">+1</button>
methods数据:
add: function (n) { //可以简写为add ()
// console.log(vm === this) 这里可以证实vm和this是相等的
// vm.count += 1
this.count += n
},
这里的add: function (n)可以简写:
add(n) {
this.count += n
}
$event事件对象
简介:当绑定事件时,要传参的同时还要在发生事件后修改DOM元素时,要用到
下面是一个根据count是否为偶数变色的样例:
html数据:
<div id="app">
<p>count的值是:{{count}}</p>
<button @click="add(1, $event)">+1</button>
<button v-on:click="sub">-1</button>
</div>
methods数据:
methods: {
add (n,e) {
this.count += n
console.log(e)
if (this.count % 2 == 0){
//偶数
e.target.style.backgroundColor = 'red'
} else {
//奇数
e.target.style.backgroundColor = ''
}
},
事件修饰符
简介:vue提供了事件修饰符的概念来辅助程序员更方便的对事件触发进行控制。
常见的5个事件修饰符:
事件修饰符 | 说明 |
---|---|
.prevent | 阻止默认行为(例如:阻止a连接的跳转、表单的提交) |
.stop | 阻止事件冒泡 |
.capture | 以捕获模式触发当前的事件处理函数 |
.once | 绑定事件只触发1次 |
.self | 只有在event.target是当前元素自身时,才触发事件处理函数 |
两个个简单的例子:
阻止a链接跳转:
html数据:
<div id="app">
<a href="http://www.baidu.com" @click.prevent="show">跳转到百度首页</a>
</div>
methods数据:
show() {
console.log('点击了 a 链接')
}
阻止冒泡:阻止触发父级事件
html数据:
<div id="app">
<a href="http://www.baidu.com" @click.prevent="show">跳转到百度首页</a>
<hr>
<div style="height: 150px;background-color: orange;padding-left: 100px;line-height: 150px;"@click="divHandler">
<button @click.stop="btnHandler">按钮</button>
</div>
</div>
methods数据:
show() {
console.log('点击了 a 链接')
},
divHandler() {
console.log('divHandler')
},
btnHandler() {
console.log('btnHandler')
}
按键修饰符(@keyup.xxx)
简介:在监听键盘事件时,我们经常需要判断详细的按键。此时可以为键盘相关的事件添加按键修饰符
一个示例:判断按下esc时清空数据,按下enter触发方法
html数据:
<div id="app">
<input type="text" @keyup.esc="clearInput" @keyup.enter="commitAjax">
</div>
methods数据:
clearInput(e) {
console.log("触发了clearInput方法")
// value可以捕获输入框的数据
e.target.value = ''
},
commitAjax() {
console.log("出发了commitAjax方法")
}
双向绑定指令(v-model)
简介:vue提供了v-model
双向数据绑定指令,用来辅助开发者在不操作DOM的情况下,快速获取表单数据
区别:v-bind
只能取数据,v-model
能取能改
注意:只有表单元素使用v-model
才有意义,或者说只要有value属性的标签就可以使用
v-model基本使用
一个对比单项绑定(v-bind)和双向绑定的例子(v-model)
html数据:
<div id="app">
<p>用户的名字是: {{username}} </p>
这是一个v-model:<input type="text" v-model="username">
<hr>
这是一个v-band:<input type="text" :value="username">
<hr>
<select v-model="city">
<option value="">请选择城市</option>
<option value="1">北京</option>
<option value="2">上海</option>
<option value="3">广州</option>
</select>
</div>
methods数据:
const vm = new Vue({
// el 指定选择器
el: '#app',
// data 对象是要渲染的数据
data: {
username:'zhangsan',
city:''
},
})
v-model指令的修饰符
修饰符 | 作用 | 示例 |
---|---|---|
.number | 自动将用户的输入值转化为数值类型 | <input v-model.number="age"/> |
.trim | 自动过滤用户输入的首尾空白字符 | <input v-model.trim="msg" /> |
.lazy | 在”change”时而非”input”时更新 | <input v-model.lazy="msg" /> |
一个使用上述v-model修饰符的例子:
html数据:
<div id="app">
<input type="text" v-model.number="n1"> + <input type.number="text" v-model="n2"> = <span> {{ n1 + n2 }} </span>
<hr>
<input type="text" v-model.trim="username">
<button @click="showName">获取用户名</button>
<hr>
<input type="text" v-model.lazy="username">
</div>
methods数据:
const vm = new Vue({
// el 指定选择器
el: '#app',
// data 对象是要渲染的数据
data: {
username:'zhangsan',
n1: 1,
n2: 2
},
methods: {
showName() {
console.log(`用户名是: "${this.username}"`)
}
}
})
条件渲染指令
简介:条件渲染指令用来辅助开发者按需控制DOM的显示与隐藏。条件渲染指令有俩个,分别是v-if
和v-show
v-if和v-show
v-show
原理:动态为元素添加或移除display:none
样式来实现元素的显示和隐藏(初始状态为true
时性能好)v-if
原理:每次动态创建或移除元素(初始状态为false
时性能好)
<!-- 希望Vue控制div -->
<div id="app">
<p v-if="flag">这是被v-if控制的元素</p>
<p v-show="flag">这是被v-show控制的元素</p>
</div>
<!-- 导入Vue -->
<script src="./vue.js"></script>
<script>
// 创建vue实例对象
const vm = new Vue({
// el 指定选择器
el: '#app',
// data 对象是要渲染的数据
data: {
// 如果flag为true()展示控制的元素
flag: true
},
})
v-if和v-else
简介:和其他语言一样,vue
也有if
和else
可以通过v-if
和v-else-if
来进行:
<div id="app">
<p v-if="flag">这是被v-if控制的元素</p>
<p v-show="flag">这是被v-show控制的元素</p>
<hr>
<div v-if="type === 'A'">优秀</div>
<div v-else-if="type === 'B'">良好</div>
<div v-else-if="type === 'C'">一般</div>
<div v-else>差</div>
</div>
<!-- 导入Vue -->
<script src="./vue.js"></script>
<script>
// 创建vue实例对象
const vm = new Vue({
// el 指定选择器
el: '#app',
// data 对象是要渲染的数据
data: {
// 如果flag为true()展示控制的元素
flag: false,
type: 'A'
},
})
</script>
v-for列表渲染
简介:vue提供了v-for 列表渲染指令,用来辅助开发者基于一个数组来循环渲染一个列表结构
例子:一个列表
<div id="app">
<table class="table table-bordered table-hover table-striped">
<thead>
<th>索引</th>
<th>ID</th>
<th>姓名</th>
</thead>
<tbody>
<!-- 这里的item和index的名字可以随意起,index为索引值,:title为标签显示 -->
<tr v-for="(item, index) in list" :title="item.name + index">
<td>{{ index }}</td>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
</tr>
</tbody>
</table>
</div>
<!-- 导入Vue -->
<script src="./vue.js"></script>
<script>
// 创建vue实例对象
const vm = new Vue({
// el 指定选择器
el: '#app',
// data 对象是要渲染的数据
data: {
list:[
{id: 1,name: '张三'},
{id: 2,name: '李四'},
{id: 3,name: '王五'}
]
},
})
</script>
v-for中key的使用:
- key的值只能是字符串或数字类型
- key的值必须具有唯一性(即: key的值不能重复)
- 建议把数据项id属性的值作为key的值(因为id属性的值具有唯一-性)
- 使用index的值当作key的值没有任何意义(因为index的值不具有唯一性)
- 建议使用v-for指令时一定要指定key的值(既提升性能、又防止列表状态紊乱)
一个利用key的例子:
<!-- 在页面中声明一个将要被 vue 所控制的 DOM 区域 -->
<div id="app">
<!-- 添加用户的区域 -->
<div>
<input type="text" v-model="name">
<button @click="addNewUser">添加</button>
</div>
<!-- 用户列表区域 -->
<ul>
<li v-for="(user, index) in userlist">
<input type="checkbox" />
姓名:{{user.name}}
</li>
</ul>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
// 用户列表
userlist: [
{ id: 1, name: 'zs' },
{ id: 2, name: 'ls' }
],
// 输入的用户名
name: '',
// 下一个可用的 id 值
nextId: 3
},
methods: {
// 点击了添加按钮
addNewUser() {
this.userlist.unshift({ id: this.nextId, name: this.name })
this.name = ''
this.nextId++
}
},
})
</script>
一个简单的品牌列表案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>品牌列表案例</title>
<link rel="stylesheet" href="./lib/bootstrap.css">
<link rel="stylesheet" href="./css/brandlist.css">
</head>
<body>
<div id="app">
<!-- 卡片区域 -->
<div class="card">
<div class="card-header">
添加品牌
</div>
<div class="card-body">
<!-- 添加品牌的表单区域 -->
<!-- form 表单元素有 submit 事件 -->
<form @submit.prevent="add">
<div class="form-row align-items-center">
<div class="col-auto">
<div class="input-group mb-2">
<div class="input-group-prepend">
<div class="input-group-text">品牌名称</div>
</div>
<input type="text" class="form-control" placeholder="请输入品牌名称" v-model.trim="brand">
</div>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary mb-2">添加</button>
</div>
</div>
</form>
</div>
</div>
<!-- 表格区域 -->
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">品牌名称</th>
<th scope="col">状态</th>
<th scope="col">创建时间</th>
<th scope="col">操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in list" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>
<div class="custom-control custom-switch">
<!-- 使用 v-model 实现双向数据绑定 -->
<input type="checkbox" class="custom-control-input" :id="'cb' + item.id" v-model="item.status">
<!-- 使用 v-if 结合 v-else 实现按需渲染 -->
<label class="custom-control-label" :for="'cb' + item.id" v-if="item.status">已启用</label>
<label class="custom-control-label" :for="'cb' + item.id" v-else>已禁用</label>
</div>
</td>
<td>{{ item.time | dateFormat }}</td>
<td>
<a href="javascript:;" @click="remove(item.id)">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 只要导入了 dayjs 的库文件,在 window 全局,就可以使用 dayjs() 方法了 -->
<script src="./lib/dayjs.min.js"></script>
<script src="./lib/vue-2.6.12.js"></script>
<script>
// 声明格式化时间的全局过滤器
Vue.filter('dateFormat', function (time) {
// 1. 对 time 进行格式化处理,得到 YYYY-MM-DD HH:mm:ss
// 2. 把 格式化的结果,return 出去
// 直接调用 dayjs() 得到的是当前时间
// dayjs(给定的日期时间) 得到指定的日期
const dtStr = dayjs(time).format('YYYY-MM-DD HH:mm:ss')
return dtStr
})
const vm = new Vue({
el: '#app',
data: {
// 用户输入的品牌名称
brand: '',
// nextId 是下一个,可用的 id
nextId: 4,
// 品牌的列表数据
list: [
{ id: 1, name: '宝马', status: true, time: new Date() },
{ id: 2, name: '奔驰', status: false, time: new Date() },
{ id: 3, name: '奥迪', status: true, time: new Date() },
],
},
methods: {
// 点击链接,删除对应的品牌信息
remove(id) {
this.list = this.list.filter(item => item.id !== id)
},
// 阻止表单的默认提交行为之后,触发 add 方法
add() {
// 如果判断到 brand 的值为空字符串,则 return 出去
if (this.brand === '') return alert('必须填写品牌名称!')
// 如果没有被 return 出去,应该执行添加的逻辑
// 1. 先把要添加的品牌对象,整理出来
const obj = {
id: this.nextId,
name: this.brand,
status: true,
time: new Date()
}
// 2. 往 this.list 数组中 push 步骤 1 中得到的对象
this.list.push(obj)
// 3. 清空 this.brand;让 this.nextId 自增 +1
this.brand = ''
this.nextId++
}
},
})
</script>
</body>
</html>
过滤器
简介:过滤器(Filters) 是vue为开发者提供的功能,常用于文本的格式化。过滤器可以用在两个地方——插值表达式和v-bind属性绑定。
注意:
- 过滤器是可以被连续调用的,它的参数是上一个函数的结果
- 过滤器可以传参
- 过滤器在vue3中被移除
过滤器应该被添加在JavaScript表达式的尾部,由“管道符”进行调用,示例代码如下:
<!-- 在双花括号中通过"管道符"调用 capitalize 过滤器,对message 的值进行格式化-->
<!--注意:这里调用过滤器展示是返回值,不调用展示的是message,相当于转译输出-->
<p>{{ message | capitalize }}</p>
<!--在v-bind 中通过“管道符"调用formatId过滤器,对rawId 的值进行格式化-->
<div v-bind:id="rawId | formatId"></div>
<body>
<div id="app">
<p>message 的值是:{{ message | capi }}</p>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: 'hello vue.js'
},
// 过滤器函数,必须被定义到 filters 节点之下
// 过滤器本质上是函数
filters: {
// 注意:过滤器函数形参中的 val,永远都是“管道符”前面的那个值
capi(val) {
// 字符串有 charAt 方法,这个方法接收索引值,表示从字符串中把索引对应的字符,获取出来
// val.charAt(0)
const first = val.charAt(0).toUpperCase()
// 字符串的 slice 方法,可以截取字符串,从指定索引往后截取
const other = val.slice(1)
// 强调:过滤器中,一定要有一个返回值
return first + other
}
}
})
</script>
</body>
私有过滤器和全局过滤器
简介:在 filters 节点下定义的过滤器,称为“私有过滤器”,因为它只能在当前 vm 实例所控制的 el 区域内使用。 如果希望在多个 vue 实例之间共享过滤器,则可以按照如下的格式定义全局过滤器:
//全局过滤器-独立于每个Vm实例之外
// Vue. filter() 方法接收两个参数:
//第1个参数,是全局过滤器的"名字”
//第2个参数,是全局过滤器的"处理函数”
Vue. filter( 'capitalize', (str) => {
return str . charAt(0). toUpperCase() + str .slice(1) +
})
注意:在实际操作过程中大多数使用全局过滤器,如果私有过滤器和全局过滤器名字冲突,按照就近原则会先调用私有过滤器,下面的例子以波浪线做区分谁调用的是全局过滤器:
<body>
<div id="app">
<p>message 的值是:{{ message | capi }}</p>
</div>
<div id="app2">
<p>message 的值是:{{ message | capi }}</p>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
// 使用 Vue.filter() 定义全局过滤器
Vue.filter('capi', function (str) {
const first = str.charAt(0).toUpperCase()
const other = str.slice(1)
return first + other + '~~~'
})
const vm = new Vue({
el: '#app',
data: {
message: 'hello vue.js'
},
// 过滤器函数,必须被定义到 filters 节点之下
// 过滤器本质上是函数
filters: {
// 注意:过滤器函数形参中的 val,永远都是“管道符”前面的那个值
capi(val) {
// 字符串有 charAt 方法,这个方法接收索引值,表示从字符串中把索引对应的字符,获取出来
// val.charAt(0)
const first = val.charAt(0).toUpperCase()
// 字符串的 slice 方法,可以截取字符串,从指定索引往后截取
const other = val.slice(1)
// 强调:过滤器中,一定要有一个返回值
return first + other
}
}
})
// ----------------------------------
const vm2 = new Vue({
el: '#app2',
data: {
message: 'heima'
}
})
</script>
</body>
侦听器
watch侦听器
简介:watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作
语法格式如下:
const Vm = new Vue({
el :
' #app' ,
data: { username:
},
watch: {
//监听username 值的变化
// newVal 是"变化后的新值”,oldVal 是"变化之前的旧值”
username(newVal, oldVal) {
console. log(newVal, oldVal)
}
})
例子:侦听用户名是否被占用
<body>
<div id="app">
<input type="text" v-model="username">
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script src="./lib/jquery-v3.6.0.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
username: 'admin'
},
// 所有的侦听器,都应该被定义到 watch 节点下
watch: {
// 侦听器本质上是一个函数,要监视哪个数据的变化,就把数据名作为方法名即可
// 新值在前,旧值在后
username(newVal) {
// 防止用户名为空的请求发送
if (newVal === '') return
// 1. 调用 jQuery 中的 Ajax 发起 get 请求,判断 newVal 是否被占用!!!
$.get('https://www.escook.cn/api/finduser/' + newVal, function (result) {
console.log(result)
})
}
}
})
</script>
</body>
应用场景:监视某个数值然后做一件事
方法&对象侦听
- 方法格式的侦听器(如上面的例子)
- 缺点1:无法在刚进入页面的时候,自动触发!!!
- 缺点2:如果侦听的是一个对象,如果对象中的属性发生了变化,不会触发侦听器!!!
- 对象格式的侦听器
- 好处1:可以通过 immediate 选项,让侦听器自动触发!!!
- 好处2:可以通过 deep 选项,让侦听器深度监听对象中每个属性的变化!!!
注意:应该先考虑最简单的方法侦听器,在考虑复杂的对象侦听器
这里的需求:打开页面后让侦听器触发一次
<body>
<div id="app">
<input type="text" v-model="username">
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script src="./lib/jquery-v3.6.0.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
username: 'admin'
},
// 所有的侦听器,都应该被定义到 watch 节点下
watch: {
// 定义对象格式的侦听器
username: {
// 侦听器的处理函数
handler(newVal, oldVal) {
console.log(newVal, oldVal)
},
// immediate 选项的默认值是 false
// immediate 的作用是:控制侦听器是否自动触发一次!
immediate: true
}
}
})
</script>
</body>
深度侦听deep
简介:如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选项,代码示例如下:
<body>
<div id="app">
<input type="text" v-model="info.username">
<input type="text" v-model="info.address.city">
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script src="./lib/jquery-v3.6.0.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
// 用户的信息对象
info: {
username: 'admin',
address: {
city: '北京'
}
}
},
// 所有的侦听器,都应该被定义到 watch 节点下
watch: {
/* info: {
handler(newVal) {
console.log(newVal)
},
// 开启深度监听,只要对象中任何一个属性变化了,都会触发“对象的侦听器”
deep: true
} */
// 如果要侦听的是对象的子属性的变化,则必须包裹一层单引号,这里‘’写了一个表达式
'info.username'(newVal) {
console.log(newVal)
},
'info.address.city'(newVal) {
console.log(newVal)
}
}
})
</script>
</body>
计算属性
简介:计算属性指的是通过一系列运算之后,最终得到一个属性值。 这个动态计算出来的属性值可以被模板结构或 methods 方法使用。
特点:
- 在定义的时候,要被定义为”方法”
- 在使用计算属性的时候,当普通的属性使用即可
好处:
- 实现了代码的复用
- 只要计算属性中依赖的数据源变化了,则计算属性会重新求值!
例子:
<body>
<div id="app">
<div>
<span>R:</span>
<input type="text" v-model.number="r">
</div>
<div>
<span>G:</span>
<input type="text" v-model.number="g">
</div>
<div>
<span>B:</span>
<input type="text" v-model.number="b">
</div>
<hr>
<!-- 专门用户呈现颜色的 div 盒子 -->
<!-- 在属性身上,: 代表 v-bind: 属性绑定 -->
<!-- :style 代表动态绑定一个样式对象,它的值是一个 { } 样式对象 -->
<!-- 当前的样式对象中,只包含 backgroundColor 背景颜色 -->
<div class="box" :style="{ backgroundColor: rgb }">
{{ rgb }}
</div>
<button @click="show">按钮</button>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
// 红色
r: 0,
// 绿色
g: 0,
// 蓝色
b: 0
},
methods: {
// 点击按钮,在终端显示最新的颜色
show() {
console.log(this.rgb)
}
},
// 所有的计算属性,都要定义到 computed 节点之下
// 计算属性在定义的时候,要定义成“方法格式”
computed: {
// rgb 作为一个计算属性,被定义成了方法格式,
// 最终,在这个方法中,要返回一个生成好的 rgb(x,x,x) 的字符串
rgb() {
return `rgb(${this.r}, ${this.g}, ${this.b})`
}
}
});
console.log(vm)
</script>
</body>
axios
简介:axios是一个专注于网络请求的库!
axios的基本使用
axios的基本语法如下:
axios({
method:'请求的类型',
url:'请求的 URL 地址'
}).then((result) =>{
// .then 用来指定请求成功之后的回调函数
// 形参中的 result 是请求成功之后的结果
})
axios请求数据
数据结构
简介:axios会对返回的数据进行封装,不仅包含了真实数据,还包含了其余字段

由于axios
封装,服务器返回的数据被放在了data
字段里
请求方式
发起 GET 请求:
axios({ // 请求方式 method: 'GET', // 请求的地址 url: 'http://www.liulongbin.top:3006/api/getbooks', // URL 中的查询参数 params: { id: 1 } }).then(function (result) { console.log(result) })
发起 POST 请求:
这里的result不是服务器真实的数据
document.querySelector('#btnPost').addEventListener('click', async function () { // 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await! // await 只能用在被 async “修饰”的方法中 const result = await axios({ method: 'POST', url: 'http://www.liulongbin.top:3006/api/post', data: { name: 'zs', age: 20 } }) console.log(result) })
要获得真实的数据就要获取
result
里的data
,如何获取data
:document.querySelector('#btnPost').addEventListener('click', async function () { // 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await! // await 只能用在被 async “修饰”的方法中 const { data } = await axios({ method: 'POST', url: 'http://www.liulongbin.top:3006/api/post', data: { name: 'zs', age: 20 } }) console.log(data) })
使用解构赋值
<body>
<button id="btnPost">发起POST请求</button>
<button id="btnGet">发起GET请求</button>
<script src="./lib/axios.js"></script>
<script>
document.querySelector('#btnPost').addEventListener('click', async function () {
// 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await!
// await 只能用在被 async “修饰”的方法中
const { data } = await axios({
method: 'POST',
url: 'http://www.liulongbin.top:3006/api/post',
data: {
name: 'zs',
age: 20
}
})
console.log(data)
})
document.querySelector('#btnGet').addEventListener('click', async function () {
// 解构赋值的时候,使用 : 进行重命名
// 1. 调用 axios 之后,使用 async/await 进行简化
// 2. 使用解构赋值,从 axios 封装的大对象中,把 data 属性解构出来
// 3. 把解构出来的 data 属性,使用 冒号 进行重命名,一般都重命名为 { data: res }
const { data: res } = await axios({
method: 'GET',
url: 'http://www.liulongbin.top:3006/api/getbooks'
})
console.log(res.data)
})
// $.ajax() $.get() $.post()
// axios() axios.get() axios.post() axios.delete() axios.put()
</script>
</body>
直接发起请求
<body>
<button id="btnGET">GET</button>
<button id="btnPOST">POST</button>
<script src="./lib/axios.js"></script>
<script>
document.querySelector('#btnGET').addEventListener('click', async function () {
/* axios.get('url地址', {
// GET 参数
params: {}
}) */
const { data: res } = await axios.get('http://www.liulongbin.top:3006/api/getbooks', {
params: { id: 1 }
})
console.log(res)
})
document.querySelector('#btnPOST').addEventListener('click', async function () {
// axios.post('url', { /* POST 请求体数据 */ })
const { data: res } = await axios.post('http://www.liulongbin.top:3006/api/post', { name: 'zs', gender: '女' })
console.log(res)
})
</script>
</body>