模块化(js的模块化、css的模块化、资源的模块化)
组件化(复用现有的ui结构、样式、行为)
规范化(目录结构的划分、编码规范化、接口规范化、文档规范化、Git分支管理)
自动化(自动化构建、自动部署、自动化测试)
webpack提供了友好的前端模块化开发支持,以及代码压缩混淆、处理浏览器端JavaScript的兼容性,性能优化等强大功能
不需要手动配,需要了解原理,实际开发中会使用命令行工具(cli)一键生成webpack的项目
在webpack4.x和5.x的版本中,有如下的默认约定
在webpack.config.js配置文件中通过entry节点指定打包的入口。通过output节点指定打包的出口
代码如下:
const path=require(‘path’) //导入专门处理操作路径的模块
module.exports={
entry:path.join(_dirname ,‘./src/index.js’) //打包入口文件的路径
output: {
path: path.join(_dirname,‘./dist’)
} //输出文件的存放路径
filename:‘bundle.js’ //输出文件的名称
}
安装webpack 和wabpack-cil
在package.json中script中改为"webpack server"
webpack-dev-server 在编译之后不会产生任何输出文件。而是将webpack输出的 bundle 文件保留在内存中,并将它们当作挂载在服务器根路径上的真实文件来提供服务。实时打包更新
类似node.js用的nodemon工具
每当修改了源代码,webpack会自动进行项目的打包和构建
webpack中HTML插件(类似于一个模板引擎插件)
可以通过此插件 自定制index.html页面的内容
在webpack.config.js配置文件中
const HtmlPlugin = require(‘html-webpack-plugin’)
const htmlPlugin = new HtmlPlugin({
template: ‘./src/index.html’,
// 指定复制出来的文件名和存放路径
filename: ‘./index.html’,
})
在moudle.exports里
entry: path.join(__dirname, ‘./src/index1.js’),
output: {
path: path.join(__dirname, ‘dist’),
filename: ‘bundle.js’
},
// 插件的数组,将来webpack在运行时,会加载并调用这些插件
plugins: [htmlPlugin],
作用:把指定的页面复制到根目录,还会自动引入打包bundle.js文件
在webpack.config.js配置文件中,通过devServer节点对webpack-dev-server插件进行更多的配置
devServer: {
open: true, //初次打包完成后,自动打开浏览器
host: ‘127.0.0.1’.//实时打包所使用的主机地址
potr: 80, //实时打包所使用的端口号
}
webpack默认只打包处理以.js后缀名结尾的模块。其他非.js后缀名结尾的模块,webpack默认处理不了,需要调用loader加载器才可以正常打包,否则会报错!
loader加载器的作用:协助webpack打包处理特定的文件模块。比如:
css-loader 可以打包处理.css相关的文件
npm i [email protected] [email protected] -D
在webpack.config.js的module ->rules数组中 ,添加loader规则如下:
module:{
rules: [
{test:/.css$/, use: [‘style-loader’,‘css-loader’]}
]
}
其中,test表示匹配的文件类型,use表示对应要调用的loader
注意:
less-loader 可以打包处理.less相关的文件
npm i [email protected] [email protected] -D命令
在webpack.config.js的module ->rules数组中 ,添加loader规则如下:
module:{
rules: [
{test:/.css$/, use: [‘style-loader’,‘css-loader’,‘less-loader’]}
]
}
babel-loader 可以打包处理webpack无法处理的高级js语法
用户请求数据会先请求html标签,再请求照片,
解决方案
: 精灵图或者base64转换成图片编码(缺点是体积会变大,适合小logo)
运行npm i [email protected] [email protected] -D 命令
在webpack.config.js的module ->rules数组中 ,添加loader规则如下:
module:{
rules: [
//如果需要调用的loader,只有一个,则只传递一个字符串也行,多个loader则必须指定数组
{test:/.jpg|png|gif$/, use: ‘url-loader?limit=22229’},
]
}
其中?之后的是loader的参数项:
webpack只能打包处理一部分的高级的JavaScript语法,对于那些webpack无法处理的高级js语法,需要借助babel-loader进行打包处理
如下webpack解决不了
//定义了名为info的装饰器
function info(target){
//为目标添加静态属性 info
target.info = ‘Person info’
}
为person类应用info装饰器
@info
class Person{}
console.log(Person.info)
解决方案:
npm i [email protected] @babel/[email protected] @babel/[email protected] -D
在webpack.config.js的module ->rules数组中 ,添加loader规则如下:
module:{
rules: [
{test:/.js$/, use: ‘babel-loader’,exclude: /node_modules/}
]
}
在项目根目录下创建babel.config.js.的配置文件 ,定义Babel的配置项如下
module.exports = {
//声明babel可用的插件
plugins:[[‘@babel/plugin-proposal-decorators’,{legacy:true}]]
}
在package.json文件的scripts节点下,新增bulid命令
“scripts”:{
“dev”: “webpack serve”, //开发环境中,运行dev命令 存在内存
“bulid”: “webpack --mode production” //项目发布时,运行bulid命令 存在物理磁盘 后端来拿
}
–mode 是一个参数项,用来指定webpack的运行模式,production代表生产环境,会对打包生成的文件进行代码压缩和性能优化
注意
:用过–mode指定的参数项,会覆盖webpack.config.js中的mode选项
在webpack.config.js配置文件的output节点中,进行配置
output:{
path:paht.join(_dirname,‘dist’)
filename:‘js/bundle.js’
}
修改webpack.config.js中的url-loader配置,新增outputPath选项即可指定图片文件的输出路径
{
text:/.jpg|png|gif$/,
use: ‘url-loader?limit=470&outputPath=images’
}
//安装插件
clean-webpack-plugin
//导入
const {CleanWebpackPlugin}=require(‘clean-wabpack-plugin’)
const cleanPlugin = new CleanWebpackPlguin()
//把创建的cleanPlugin插件实例对象。挂载到plugins节点
plugins:[htmlPlugin,cleanPlugin]
是一个信息文件,里面存储着位置信息,Source Map文件中存储着压缩混淆后的代码,所对应的转换前的位置
出错的时候,除错工具将直接显示原始代码,而不是转换后的代码,能够极大地方便后期的调试
在webpack.config.js中添加如下的配置,即可保证运行时报错的行数与源代码的行数一直保持一致,也展示源码
module.exports = {
mode: ‘development’,
//eval-source-map 仅限在"开发模式"下使用,不建议在生产模式下使用
//此选项生产的Source Map能够保证"在运行时报错的行数"与"源代码的行数"保证一致
devtool: eval-source-map’,
}
在生产环境下,如果省略了devtool选项,则最终生产的文件中不包含Source Map。这能够防止原始代码通过Source Map的形式暴露
建议设置为devtool: ‘nosources-source-map’,或者直接关闭Source Map
devtool: ‘source-map’,
会产生.map文件
建议使用@从外往里查找,不要使用…/从里往外查找
用前要配置,不用手动配
在webpack.config.js下
module.exports = {
resolve: {
alias:{
‘@’: path.join(__dirname,‘./src/’)
}
}
}
在ES6模块化规范之前。JavaScript社区尝试并提出了AMD、CMD、CommonJS等模块化规范
但存在一定的差异性和局限性,并不是浏览器与服务器通用的模块化标准
AMD和CMD适用于浏览器端
的JavaScript模块化
CommonJS适用于服务器端的JavaScript模块化
ES6模块化是浏览器端与服务器端通用的模块化开发规范
默认导出与默认导入
导出: export default {默认导出的成员}
导入:import 接收名称 from ‘模块路径’
注意事项:
每个模块中,只允许使用唯一的一次export default, 否则会报错
接收名称任意取值,但不能以数字开头
按需导出与按需导入
export let s1=‘aaa’
export function say() {}
import {s1 as s11} from ‘模块路径’
注意事项:
每个模块中可以多次按需导出
按需导入的成员名称必须和按需导出的名称保持一致
按需导入时,可以使用as关键字进行重命名
按需导入可以和默认导入一起用
直接导入并执行模块中的代码
只想执行某个模块中的代码,并不需要得到模块中向外共享的成员
for( let i =0; i<3;i++){
console.log(i)
}
import ‘路径’
多层回调函数的相互嵌套,就形成了回调地狱
setTimeout(()=>{
console.log(‘延时1秒后输出’)
setTimeout(() => {
console.log (‘延时2秒后输出’)
setTimeout(() =>{
console.log(‘延时3秒后输出’)
}。3000)
},2000)
},1000)
缺点:
造成回调地狱
node.js官方提供的fs模块仅支持以回到函数的方式读取文件,不支持Promise的调用方法。因此。需要安装
then-fs这个第三方包,从而支持基于Promise的方式读取文件的内容
调用then-fs提供的readFile()方法,可以异步读取文件的内容,它的返回值是Promise的实例对象。
因此可以调用.then()方法为每个Promise异步操作指定成功与失败之后的回调函数
import thenFs from ‘then-fs’
thenFs.readFile(‘./files/1.txt’,‘utf-8’),then(r1=>{console.log(r1)},err =>{console.log(err1.message)}}
thenFs.readFile(‘./files/2.txt’,‘utf-8’),then(r2=>{console.log(r1)},err =>{console.log(err2.message)}}
thenFs.readFile(‘./files/3.txt’,‘utf-8’),then(r3=>{console.log(r1)},err =>{console.log(err3.message)}}
**注意:**上述的代码无法保证文件的读取顺序,需要进一步改进
Promise支持链式调用,从而解决回调地狱的问题
thenFs.readFile(‘./files/1.txt’,‘utf8’)
.then((r1) => {
console.log(r1)
return thenFs.readFile(‘./files/2.txt’,‘utf8’)
})
.then((r2) => {
console.log(r2)
return thenFs.readFile(‘./files/3.txt’,‘utf8’)
})
.then((r3) => {
console.log(r3)
})
.catch(err =>{
console.log(err.message)
})
如果不希望前面的错误导致后续的.then无法正常执行,则可以.catch的调用提前
thenFs.readFile(‘./files/1.txt’,‘utf8’)
.catch(err =>{
console.log(err.message)
})
.then((r1) => {
console.log(r1)
return thenFs.readFile(‘./files/2.txt’,‘utf8’)
})
.then((r2) => {
console.log(r2)
return thenFs.readFile(‘./files/3.txt’,‘utf8’)
})
.then((r3) => {
console.log(r3)
})
会发起并行的Promise异步操作,等所有的异步操作全部结束后才会执行下一步的.then操作(等待机制 )
const promiseArr = [
thenFs.readFile(‘./files/1.txt’,‘utf-8’),
thenFs.readFile(‘./files/2.txt’,‘utf-8’),
thenFs.readFile(‘./files/3.txt’,‘utf-8’)
]
Promise.all(promiseArr)
.then(([r1,r2,r3]) => {
console.log(r1.r2.r3)
})
.catch(err => {
console .log(err.message )
})
会发起并行的Promise异步操作,只要任何一个异步操作完成,就立即执行下一步的.then操作(赛跑机制)。
const promiseArr = [
thenFs.readFile(‘./files/1.txt’,‘utf-8’),
thenFs.readFile(‘./files/2.txt’,‘utf-8’),
thenFs.readFile(‘./files/3.txt’,‘utf-8’)
]
Promise.race(promiseArr)
.then(([r1,r2,r3]) => {
console.log(r1.r2.r3)
})
.catch(err => {
console.log(err.message )
})
方法的封装要求
方法的名称要定以为getFile
方法接收一个形参fpath,表示要读取的文件路径
方法返回值为Promise实例对象
import fs from ‘fs’
function getFile(fpath){
return new Promise(function(resolve,reject){ //如果()里没有function那只是创建了一个形式上的异步操作
fs.readFile(fpath,‘utf8’,(err,dataStr ) => {
if(err) return reject(err)
resolve(dataStr)
})
})
}
getFile(‘./files/1.txt’).then((r1)=>{console.log(r1)},(err) =>{console.log(err.message)})
或者
getFile(‘./files/1.txt’).then((r1)=>{console.log(r1)})
.catch((err) => console.log(err.message))
是es8引入的新语法,用来简化Promise异步操作。在async/await出现之前,开发者只能用.then()方法处理异步
async function getAllFile() {
const r1 = await thenFs.readFile(‘./files/1.txt’,‘utf8’) //r1会获取到文件内容
console.log(r1)
const r2 = await thenFs.readFile(‘./files/3.txt’,‘utf8’)
console.log(r2)
const r3 = await thenFs.readFile(‘./files/3.txt’,‘utf8’)
console.log(r3)
}
getAllFile()
注意事项:
如果在function中使用了await,则function必须被async修饰
在async方法中,第一个await之前的代码会同步执行,await之后的代码会异步执行
console.log(‘A’)
async function getAllFile() {
console.log(‘B’)
const r1 = await thenFs.readFile(‘./files/1.txt’,‘utf8’)
const r2 = await thenFs.readFile(‘./files/2.txt’,‘utf8’)
const r3 = await thenFs.readFile(‘./files/3.txt’,‘utf8’)
console.log(r1,r2,r3)
console.log(‘D’)
}
getAllFile()
console.log(‘C’)
输出结果:
A
B
C
111 222 333
D
JavaScript是单线程的语言
如果前一个任务非常耗时,则后续的任务就不得不一直等待,从而导致程序假死的问题
为了防止此问题,JavaScript把待执行的任务分为了两类
例子:
import thenFs from ‘then-fs’
console.log(‘A’)
thenFs.readFile(‘./files/1.txt’,‘utf8’).then(dataStr => {
console.log(‘B’)
})
setTimeout(() =>{
console.log(‘C’)
},0)
console.log(‘D’)
输出
ADCB
JavaScript把异步任务又做了进一步的划分
宏任务(macrotask)
微任务(mincrotask)
${变量}
用于构建用户界面的前端框架
在使用了vue的页面中,vue会监听数据的变化,从而自动重新渲染页面的结构。
注意:数据驱动视图是 单向的数据绑定
在网页中,form表单负责采集数据,Ajax负责提交数据
在填写表单时,双向数据绑定可以辅助开发者在不操作DOM的前提下,自动把用户填写的内容同步到数据源中
是vue实现数据驱动视图和双向数据绑定的核心原理,它把每个HTML页面都拆成如下三个部分:
View 表示当前页面所渲染的DOM结构
Model 表示当前页面渲染时所依赖的数据源(假数组,ajax请求过来的数组)
ViewModel 表示vue的实例,是MVVM的核心
vue3新增功能例如:
组合式API、多根节点组件、更好的Typescript支持等
废弃的旧功能:
过滤器、不再支持 o n , on, on,off和$once实例方法等
指令是vue为开发者提供的模板语法,用于辅助开发者渲染页面的基本结构,有六大类
辅助开发者渲染DOM元素的文本内容
v-bind:placeholder=“tips”
v-bind可以简写成: // :placeholder=“tips”
也支持JavaScript表达式的运算
例子:
例子: +n
在vm的实例对象中
添加 methods: {
add: function (n) { //:funtion 可省略 add(n){}
console.log(‘ok’)
//vm.count +=n
this.count +=n
}
}
可以简写成@click
如果不传参,可以用e来接收,e是事件的对象,可以修改e,target.style.属性
vue的内置变量,就是原生DOM的事件对象e
例子:+1
add: function (n, e) {
// vm.count += 1
this.count += 1
if (this.count % 2 == 0) {
e.target.style.backgroundColor = ‘red’
} else {
e.target.style.backgroundColor = ‘’
}
}
事件修饰符 | 说明 |
---|---|
.prevent | 阻止默认行为(例如:阻止a链接跳转,阻止表单的提交等) |
.stop | 阻止事件冒泡 |
//只有在按下Esc时调用’vm.clearInput()’
//只有在按下Enter时调用’vm.submit()’
用来辅助开发者不操作DOM的前提下,快速获取表单的数据(用于表单或者select)
修饰符 | 作用 | 示例 |
---|---|---|
.number | 自动将用户的输入值转为数值类型 | |
.trim | 自动过滤用户输入的首尾空白字符 | |
.lazy | 在"change"时而非input 时更新 |
用来辅助开发者按需控制DOM的显示和隐藏
动态创建和移除DOM来显示和隐藏,适用于默认不需要展示,而且后期很可能也不需要展示,v-if性能更好
用过display属性来控制显示和隐藏,适用于频繁显示隐藏的情况,性能更好
在实际开发,绝大多数情况下,不考虑性能,直接使用v-if
用来辅助开发者基于一个数组渲染一个列表结构
需要使用item in items形式的特殊语法
items是待循环的数组
item是被循环的每一项
v-for=“(item,idex) in items” //另一种格式
官方建议:只要用到了v-for,需要绑定一个:key属性
Filter,本质上是一个函数,常用于文本的格式化,可以用在:插值表达式和v-bind属性绑定
过滤器应该被添加到js表达式的尾部,由"管道符"进行调试
例子:
{{message | Filter}}
v-bind:id=“Id | Filter”
(与data同级)filters:{
//形参val,永远都是"管道符"前面的值
Fliter(val){
//一定要有返回值
return
}
}
定义在vm实例内,代码如上
vue.filter(‘参数1’, function(str) {
return str.charAt(0).toUpperCase() + str.slice(1) +‘~~’
})
参数1: 是全局过滤器的名字
参数2: 是全局过滤器的处理函数
注意:
如果全局过滤器和私有过滤器的名字相同,此时按照"就近原则",调用私有过滤器
多个过滤器使用
message|xxx|yyy|jjj
过滤器可以传参
message |filterA(arg1,arg2)
vue.flilter(‘filterA’,(message,arg1,arg2)) =>{
}
vue3,官方建议使用计算属性或方法代替过滤器功能
针对数据的变化做特定的操作
v-model=‘username’
data: {
username: ‘admin’
}
watch:{
//侦听器本质是一个函数。要监视哪个数据的变化,就把数据名作为方法名即可
//newVal是’变化后的新值’,oldVal是’变化之前的旧值’
username(newVal,oldVal){
}
}
方法格式的监听器(上述代码)
对象格式的监听器
watch: {
username: {
//handler是固定写法
handler(newVal,oldVal){
console.log(newVal,oldVal)
},
//表示页面初次渲染好之后,就立即触发当前的watch侦听器
immediate:true //默认值是false
}
//侦听的值为对象时
watch: {
info: {
handler(newVal){
console.log(newVal,oldVal)
},
deep: true // 开启深度监听,只要对象中任何一个属性变化了,都会触发"对象的侦听器"
//如果要侦听的是对象的子属性,则必须包裹一层单引号
'info.username'(){
console.log
}
}
通过一系列运算之后,最终得到一个属性值
这个动态计算出来的属性值可以被模板结构或methods方法使用
//所有的计算属性都要定义到computer节点下
//计算属性在定义的时候,要定义成"方法格式" 调用时是属性
computed:{
rgb(){ return rgb(${this.r},${this.g},${this,b})
}//es6模板字符串
},
methods: {
show(){ console.log(this.rgb)}
}
好处:
axios 是一个专注于网络请求的库
1. //调用axios 方法得到的是promise对象
document.querySelector("#btnpost").addEventListener('click', async function(){
//{}解构赋值,从axios封装的大对象中,把data属性结构出来,使用:重命名
const {data : res} = await axios({
method:'POST',
url: 'http://www.liulongbin.top:3006/api/getbooks',
params:{
id=1
}
})
// console.log(data.data)
console.log(res.data)
})
1. document.querySelector("#btnpost").addEventListener('click', async function(){
//{}解构赋值,从axios封装的大对象中,把data属性结构出来,使用:重命名
const {data : res} = await axios({
method:'POST',
url: 'http://www.liulongbin.top:3006/api/getbooks',
data: {
name:'zs',
age:20
}
})
// console.log(data.data)
console.log(res.data)
})
<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>
简称SPA,指的是一个web网站中只有唯一的一个html页面,所有的功能与交互都在这唯一的一个页面完成
vue-cli是vue开发的标准工具,它简化了程序员基于webpack创建工程化的vue项目的过程
npm i -g @vue/cli
vue create 项目名称
鼠标在cmd窗口乱画会冻结导致暂停,解冻:点击cmd的窗口 +ctrl+c
1 assets 文件夹:存放项目中用到的静态资源文件,例如:css样式表、图片资源等等
2 components 文件夹:程序员封装的、可复用的组件,都要放到此目录下
3 main.js 是项目的入口文件。整个项目的运行,要先执行main.js
4 App.vue 是项目的根组件
在工程化项目中,vue通过main.js把App.vue渲染到index.html的指定区域中(替换)
el: ‘#app’, 与.$mount(‘#app’)作用一致
组件化开发:根据封装的思想,把页面上可重用的UI结构封装为组件,从而方便项目的开发和维护
vue是一个支持组件化开发的框架
vue中规定,组件的后缀名是.vue。App.vue本质上就是一个vue的组件
智能组件(数据获取,数据过滤…)
木偶组件(渲染视图)
template -> 组件的模板结构 (必须有)
只能有一个根节点(最外面只能有一个div)
script -> 组件的JavaScript行为(可选)
style -> 组件的样式(可选)
render函数中,渲染的是哪个.vue组件,那么这个组件就叫做"根组件"
组件再被封装好后,彼此之间是相互独立,在使用组件的时候,根据彼此的嵌套关系,形成了父子关系、兄弟关系
使用import语法导入需要的组件
使用components节点注册组件
以标签形式使用刚才注册的组件(使用的时候就创建了一个对象实例)
在vscode里的settings.json中添加
"path-autocomplete.extensionOnImport": true,
"path-autocomplete.pathMappings": {
"@": "${folder}/src"
},
在vue.app就可以用@引用文件
子组件不能用在别的组件
在vue项目的main.js入口文件中,通过Vue.components()方法,可以注册全局组件
import xxx from “yyy”
Vue.components(“变量名”,xxx)
props是组件的自定义属性,在封装通用组件的时候,合理使用props可以极大提高组建的复用性
可定义成数组或者对象
export default {
//是自定义属性,允许使用者通过自定义属性,为当前组价指定初始值
props:['init']
}
//这是字符串"9",想转换成数字, :init=“9”
不能直接修改props的值
解决方案,把props 赋值到data里
props:['init']
data(){
return{this.init}
}
export default {
props:{
init:{
//用default 属性定义属性的默认值
default:0
//用type 属性定义属性的值类型
//如果传递过来的值不符合此类型,则会在终端报错
//可用[Number,String]定义多个类型
type:Number
//必填项校验
require:true
}
//在使用组件中,如果某个属性名是小驼峰形式,cmtCount,则绑定属性值时建议改写成
连字符格式,cmt-count
}
}
默认情况下,写在.vue组件中的样式会全局生效,因此会造成多个组件之间的样式冲突问题
在style标签里添加 scoped属性
原理:scoped会给template里标签添加 data-v-n属性
当使用第三方组件库的时候,如果有修改第三方组件默认样式的需求,需要用到/deep/
/deep/ h3
每个.vue都交给vue-template-compiler包编译成js
生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个过程,强调的是一个时间段
生命周期函数:是由vue框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行
在created阶段,会使用created函数调用methods中的方法,发起ajax请求拿数据,并且把请求到的数据转存到data中,供template模板渲染的时候使用
method(){
const xml = new
},
creater(){
this.initbooks()
}
在数据发生变化之后,为了能够操作最新的DOM结构,必须把代码写到updated生命周期函数中
父组件向子组件共享数据,需要使用自定义属性
//父组件
data(){
return {
message:'hello vue.js',
userinfo:{name:'zs',age:20}
}
}
//子组件
Son 组件
父组件传递过来的msg值是:{{msg}}
父组件传递过来的user值是:{{user}}
props:['msg','user']
子组件向父组件共享数据,使用自定义事件
//子组件
methods: {
add() {
// 让子组件的count值自增
this.count += 1;
// 把自增的结果,传给父组件
this.$emit("numchange", this.count);
},
},
//父组件
App 根组件 --- {{ countFromSon }}
methods: {
// 获取子组件传递过来的数据
getNewCount(val) {
console.log("numchange 事件被触发了!", val);
this.countFromSon = val;
},
},
在vue2中,兄弟组件之间数据共享的方案是EventBus。
EventBus的使用步骤
//发送方
import bus from './eventBus.js'
data(){
return {
str: `黑云压城城欲摧,渚青沙白鸟飞回。借问酒家何处是,半江瑟瑟半江红!`,
}
},
methods: {
send() {
// 2. 通过 eventBus 来发送数据
bus.$emit('share', this.str)
}
}
//接收方
{{ msgFromLeft }}
import bus from "./eventBus.js";
data() {
return {
msgFromLeft: "",
};
},
created() {
// 2.
bus.$on("share", (val) => {
console.log("", val);
this.msgFromLeft = val;
});
},
ref用来辅助开发者在不依赖于jQuery的情况下,获取的DOM元素或组件的引用
每个vue的组件实例中,都包含一个$refs对象,里面存放着对应的DOM元素或组件的引用。
默认情况下,组建的$refs指向一个空对象
《h3 ref=“myh1”>ref的名字不能重复
this.$refs.myh1.style.color=‘red’
父组件可以通过ref控制子组件
例子
//有Left子组件,com+子组件名
《Left ref=“comLeft”>
onReset(){
this.$refs.comLeft.方法()
}
此方法会把cb回调退出到下一个DOM更新周期止之后执性。也就是等组件的dom更新完成之后,在执行cb回到函数,从而确保cb回调函数可以操作到最新的DOM元素
this.$nextTick(() =>{
this.$refs.iptRef.focus()
})
some
arr.some((item,index)=>{
if(item=="){
//再找到对应的项之后,可以通过return true固定的语法,来终止some循环
return true
}
})
ever
//需求:判断数组中是否被全选了 (例子:全选)
const result =arr.ever(item => item.state)
reduce
//需求:把购物车数组中,已勾选的物品,总价累加起来
方法一:
let amt=0
arr.filter(item => item.state).forEach(item =>{
amt +=item.price * item.count
})
方法二:
//arr.filter(item => item.state).reduce((累加的结果,当前循环项)={},初始值)
const result = arr.filter(item => item.state).reduce((amt,item)={
return amt +=item.price * item.count
},0)
//简写:
const result = arr.filter(item => item.state).reduce((amt,item)=> amt+=item.price * item.count),0)
动态组件指的是动态切换组件的显示与隐藏
vue提供了一个内置组件,专门用来实现动态组件的渲染
data(){
//1.当前要渲染的组件名称
return {comName:'Left'}
}
//2.通过is属性,动态指定要渲染的组件
//3.点击按钮,动态切换组件的名称
上述代码,切换Right时,Left会被销毁,切换回来时会重新创建,但之前的数据会被初始化
当组件被缓存的时候,会自动触发组件的deactivated生命周期函数
当组件被激活的时候,会自动触发组件的activated生命周期函数
用来指定:只有名称匹配的组件会被缓存。多个组件名之间使用英文的逗号隔开
用来指定: 哪些组件不需要被缓存
注意:
命名:
在父组件中"声明组件"的时候,没有在子组件中指定name名称,则组件的名称默认就是在components下注册的名称
对比:
组件的"注册名称"的主要应用场景是:以标签的形式,把注册好的组件,渲染和使用到页面结构之中
组件声明的时候的"name"名称的主要应用场景:结合标签实现组件缓存功能;以及在调试工具中看到组件的 name 名称
插槽(Slot)是vue为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽(相当于子传父)
vue官方规定:每一个slot插槽,都要有一个name名称
如果省略了slot的name属性,则有一个默认名称叫做default
《slot name=“default”>
#名字
如果要放默认内容,放到slot标签内
有自己的name名称就是个具体插槽,为了告诉用户
为预留的slot提供属性对应的值,这种用法叫做"作用域插槽"
官方标准: obj改为scope
在directives节点中声明
directives: {
//定义名为color的指令,指向一个配置对象
//当指令第一次被绑定到元素上的时候,会立即触发bind函数
color:{
bind(el){ //形参中的el是绑定了此指令的、原生的DOM对象 固定写法
el.style.color='red'
}
}
}
// 调用
App组件
//如果v-color="blue",需要在data里定义color
//另一种写法
directives: {
//定义名为color的指令,指向一个配置对象
color:{
bind(el,bind){ //形参中的el是绑定了此指令的、原生的DOM对象 固定写法
el.style.color=bind.value
}
}
}
// 调用
App组件
//在DOM更新的时候,会触发update函数
update(el,bind) {
el.style.color=binding.value
}
如果bind和update函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式
color(el,binding){
el.style.color = binding.value
}
Vue.directive() 在main.js 进行声明
Vue.directive('color',function(el,binding){
el.style.color=binding.value
})
可组装的JavaScript和jsx检查工具
详情请看ESlink中文网
配置和axios代码在vue/demo-3
路由:Hash地址与组件之间的对应关系
Hash地址: #+地址
created(){
//底层原理
//只要当前的app组件被创建,就立即监听windows对象的onhashchange事件
windows.onhashchange = () => {
switch(location.hash){
case '#/home':
this.comName = 'Home'
break
case '#/movie':
this.comName = 'Movie'
break
case '#/about':
this.comName = 'About'
break
}
}
}
安装vue-router包
npm i [email protected] -S
创建路由模块
会自动生成
//1.导入Vue和VueRooter
import Vue from 'vue'
import VueRouter from 'vue-router'
//2.调用Vue.use()函数,把VueRouter安装为Vue的插件
Vue.use(VueRouter)
//3.创建路由的实例对象
const router = new VueRouter()
//4.向外共享路由的实例对象
export default router
导入并挂载路由模块
import router from '@/router/index.js'
new Vue({
render: h => h(App),
//在vue项目中,要想用路由,必须把路由实例对象,通过下面的方式进行挂载
router:router
}).$mount('#app')
声明路由链接和占位符
//在app.use
//在router/index.js
import Home from '@/components/Home.vue'
import Movie from '@/components/Movie.vue'
import About from '@/components/About.vue'
const router = new VueRouter({
//routes 是一个数组,作用:定义 hash地址 与组件之间的对应关系
routes: [
//路由规则
{ path: '/home', component: Home },
{ path: '/movie', component: Movie },
{path:'/About',component:About}
]
})
《a href=“#/home”>首页
《router-link to=“/home”>首页
指的是:用户在访问地址A的时候,强制用户跳转到地址C,从而展示特定的组件页面
redirect属性
routes: [
//路由规则
//重定向的路由规则
{paht:'/',redirect:'/home'}
{ path: '/home', component: Home },
{ path: '/movie', component: Movie },
{path:'/About',component:About}
]
通过路由实现组件的嵌套展示
在父组件中
《router-link to=“/home”>首页
《router-view>
通过children属性声明子路由规则
{
path: '/about', component: About, children:
[
{ path: 'tab1', component: Tab1 },
{ path: 'tab2', component: Tab2 }
]
}
如果children数组中,某个路由规则的path值为空字符串,则这条路由规则,叫做"默认子路由"
{
path: '/about', component: About, children:
[
{ path: '', component: Tab1 },
{ path: 'tab2', component: Tab2 }
]
}
动态路由值得是:是Hash地址中可变的部分定义为参数项,从而提高路由规则的复用性
在vue-router中使用英文的冒号(:)来定义路由的参数项。
{ path: '/movie/:mid', component: Movie },
this.$route.params.mid
//可以查找到,this.$route是路由的"参数对象"
//this.$router是路由的导航对象
{ path: '/movie/:mid', component: Movie,props: true },
props:['mid']
- 在hash地址中,/后面的参数项,叫做“路径参数”
在路由参数对象中,需要使用this.$route.params.query来访问路径参数
- 在hash地址中,?后面的参数项,叫做”查询参数“
- 在this.$route中 ,path只是部分路径,fullpath是完整路径
在浏览器中,点击链接实现导航的方式,叫做声明式导航
在浏览器中,调用API方法实现导航的方式,叫做编程式导航
最常用
this.$router.push(‘hash地址’)
this.$router.replace(‘hash地址’)
(数值n)
可以在浏览器中前进和后退
go(-1)表示后退一层
//如果后退的层数超过上限,则原地不动
go简化用法
导航守卫可以控制路由的访问权限
每次发生路由的导航跳转时,都会触发全局前置守卫
//创建路由实例对象
const router = new VueRouter({})
//调用路由实例对象的beforeEach方法,即可声明"全局前置守卫"
//每次发生路由导航跳转的时候,都会自动触发fn这个 回调函数
router.beforeEach(fn)
//创建路由实例对象
const router = new VueRouter({})
//调用路由实例对象的beforeEach方法,即可声明"全局前置守卫"
//每次发生路由导航跳转的时候,都会自动触发fn这个 回调函数
router.beforeEach((to,from,next)=>{
//to是将要访问的路由的信息对象
//from是将要离开的路由的信息对象
//next是一个函数,调用next()表示放行,允许这次路由导航
})
当前用户拥有后台主页的访问权限,直接放行: next()
当前用户没有后台主页的访问权限,强制其跳转到登录页面: next(‘/login’)
当前用户没有后台主页的访问权限,不允许跳转到后台主页: next(false)
下载vant,后面加-force
Element UI
View UI
Mint UI
Vant
npm i element-ui -S
在项根目录下创建vue.config.js的配置文件
如下声明
module.exports = {
devServer:{
//当前项目在开发调试阶段
//会将任何未知请求(没有匹配到静态文件的请求)代理到httpxxx
proxy:'httpxxx',
}
}
注意:
- devServer.proxy提供的代理功能,仅在开发调试阶段生效
- 项目上线发布时,依旧需要API接口服务器开启CORS跨域资源共享
开源的分布式版本控制系统
缺点:占用磁盘空间较大
优点:版本切换时非常快
工作区
暂存区
Git仓库
已修改(modified)
表示修改了文件,但还没将修改的结果放到存放区
已暂存(staged)
表示对已修改的文件的当前版本做了标记,使之包含在下次提交的列表中
已提交(committed)
表示文件已经安全地保存在本地的Git仓库中
设置自己的用户名和邮件地址
git config --global user.name “itheima”
git config -global user.email “[email protected]”
注意:如果使用了 --global选项,那么该命令只需要运行一次,即可永久生效
c盘.gitconfig
git config --list --global
git config user.name
git config user.email
可以使用git help git help config git config -h 如果自己有一个尚未进行版本控制的项目目录,想要用Git来控制它,需要执行如下两个步骤: git init命令会创建一个名为.git的隐藏目录,这个.git目录就是当前项目的Git仓库,里面包含了初始的必要文件,这些文件是Git仓库的必要组成部分 工作区中的每一个文件可能有4种状态,这四种状态共分为两大类 未跟踪<------->未被Git管理 <------------>已被Git管理{未修改-已修改-已暂存} Git操作的终极结果:让工作区中的文件都处于"未修改"的状态 git status命令查看文件处于什么状态, 在状态报告中可以看到新建的index.html文件出现在Untracked files (未跟踪的文件) 下面 未跟踪的文件意味着Git 在之前的快照(提交)中没有这些文件;git不会自动将之纳入跟踪范围,除非明确地告诉它"我需要使用Git跟踪该文件" 使用git status 输出的转态报告很详细,但有些繁琐。如果希望以精简的方式显示文件的状态, 如下两条完全等价的命令,其中-s是 --short的简写形式: git status -s git status --short ??未跟踪文件(前面有红色的?) git add 开始跟踪一个文件,所以,要跟踪index.html文件 此时再运行git.status命令,会看到index.html文件在Changes to be committed,说明已被跟踪,并处于暂存转态; 新添加到暂存区中的文件前面有绿色的A标记 index.html文件等待被提交到Git仓库中进行保存。可以执行git commit 命令进行提交, 所有 -m选项后面是本次的提交消息,用来对提交的内容做进一步的描述: git commit -m “新建了index.html文件” 证明工作区中所有的文件都处于"未修改"的状态,没有任务文件需要被提交 再次运行git status 和 git satus -s 文件index.html出现在Changes not staged for commit 这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区 需要再次运行git add 这个命令有三个功能 再次运行 git commit -m "提交信息"命令,即可将暂存区的记录的index.html的快照,提交到Git仓库中进行保存 把对工作区中对应文件的修改、还原成Git 仓库中保存的版本 操作的结果:所有的修改会丢失,且无法恢复!危险性比较高,请谨慎操作 git checkout – index.html 如果需要被暂存的文件个数比较多,可以使用如下的命令,一次性将所有的新增和修改过的文件加入暂存区: git add . 如果需要从暂存区中移除对应的文件,可以使用如下的命令: git reset HEAD 要移除的文件名称 git reset HEAD . 全部文件 标准流程:工作区-> 暂存区 ->Git仓库 工作区->Git仓库 git commit -a -m “描述信息” 从Git仓库和工作区中同时移除对应的文件 git rm -f index.js 只从Git仓库中移除指定的文件,但保留工作区中对应的文件 git rm --cached index.css 有些文件无需纳入Git的管理,也不希望它们总出现在未跟踪的文件列表 创建名为.gitignore的配置文件,列出要忽略的文件的匹配模式 例子: 是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享 一般情况下只有组件之间的数据共享才有必要存储到vuex中,对于组件中的私有数据,依旧存储在组件自身的data中 npm i vuex --save import Vuex from ‘Vuex’ Vue.use(Vuex) const store = new Vuex.Store({ //state 中存放的就是全局共享的数据 state: {count: 0} }) 将store对象挂载到vue实例中 new Vue ({ el:‘#app’, render: h=> h(app), router, //将创建的共享数据对象,挂载到vue实例中 //所有的组件,就可以从store中获取全局的数据了 store }) state提供唯一的公共数据源,所有共享的数据都要统一放到store的state中进行储存 //创建store数据源,提供唯一公共数据 const store = new Vuex.Store({ //state 中存放的就是全局共享的数据 state: { count: 0 }, mutations:{}, actions:{} }) 组件访问State中数据的第一种方式: this. s t o r e . s t a t e . 全 局 数 据 名 称 / / 例 如 t h i s . store.state.全局数据名称 //例如 this. store.state.全局数据名称//例如this.store.state.count 组件访问State中数据的第二种方式: import { mapState } from ‘vuex’ 通过导入的mapState函数,将当前组件需要的全局数据,映射为当前组件的computed计算属性: computed:{ …mapState([‘count’]) } Mutations用于变更Store中的数据 只有mutations中定义的函数,才有权利修改State中的数据 this.$store.commit()是触发mutations的第一种方式 //定义 Mutations const store = new Vuex.Store({ //state 中存放的就是全局共享的数据 state: { count: 0 }, mutations:{ add(state){ //变更状态 state.count++ } }, actions:{} }) //在组件中触发mutation methods: { handlel:{ //触发mutations的第一种方式 this.$store.commit(‘add’) } } 可以在触发mutations时传递参数 const store = new Vuex.Store({ //state 中存放的就是全局共享的数据 state: { count: 0 }, mutations:{ addN(state,step){ //变更状态 state.count += step } }, actions:{} }) //在组件中触发mutation methods: { handlel2:{ //触发mutations的第一种方式 this.$store.commit(‘addN’,3) } } 第二种方式 import { mapMutations } from ‘vuex’ 通过导入的mapMutations函数,映射为当前组件的methods函数 methods:{ …mapMutations([‘add’,‘addN’]) } mutations中不能使用异步操作(定时器等),可以再action中使用 Actions用于处理异步任务 如果通过异步操作变更数据,必须通过Actions,而不能使用Mutations,但是在Actions中还是要通过触发Mutations方式间接变更数据 this.$store.dispatch()触发actions的第一种方式 //定义 Action const store = new Vuex.Store({ //state 中存放的就是全局共享的数据 state: { count: 0 }, mutations:{ add(state){ //变更状态 state.count++ } }, actions:{ addAsync(context){ setTimeout( () => { //在 Actions中,不能直接修改state中的数据; //必须通过context.commit()触发某个mutations才行 context commit(‘add’) },1000) } } }) //触发Actions methods: { handle(){ //触发actions的第一种方式 this.$store.dispatch(‘addAsync’) } } 触发actions异步任务时携带参数 //定义 Action const store = new Vuex.Store({ //state 中存放的就是全局共享的数据 state: { count: 0 }, mutations:{ add(state,step){ //变更状态 state.count += step } }, actions:{ addAsync(context,step){ setTimeout( () => { //在 Actions中,不能直接修改state中的数据; //必须通过context.commit()触发某个mutations才行 context.commit(‘add’,step) },1000) } } }) //触发Actions methods: { handle(){ //触发actions的第一种方式 this.$store.dispatch(‘addAsync’,5) } } 第二种 import { mapActions } from ‘vuex’ 通过导入的mapActions函数,将需要的actions函数,映射为当前组件的methods函数 methods:{ …mapActions([‘addASync’,‘addNAsync’]) } Getter用于对store中的数据进行加工处理形成新的数据 //定义 Getter const store = new Vuex.Store({ state: { count: 0 }, getters: { showNum: state => { return ‘当前最新的数量是【’+ state.count +‘】’ } } }) 使用方式 this.$store .getters.名称 import { mapGetters } from ‘vuex’ computed:{ …mapGetters([‘showNum’]) } 当遇见大型项目时,数据量大,store就会显得很臃肿 为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割: 默认情况下,模块内部的 action 和 mutation 仍然是注册在全局命名空间的——这样使得多个模块能够对同一个 action 或 mutation 作出响应。 如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。 import { mapMutations } from ‘vuex’ 通过导入的mapMutations函数,映射为当前组件的methods函数 methods:{ …mapMutations([‘add’,‘addN’]) } mutations中不能使用异步操作(定时器等),可以再action中使用 Actions用于处理异步任务 如果通过异步操作变更数据,必须通过Actions,而不能使用Mutations,但是在Actions中还是要通过触发Mutations方式间接变更数据 this.$store.dispatch()触发actions的第一种方式 //定义 Action const store = new Vuex.Store({ //state 中存放的就是全局共享的数据 state: { count: 0 }, mutations:{ add(state){ //变更状态 state.count++ } }, actions:{ addAsync(context){ setTimeout( () => { //在 Actions中,不能直接修改state中的数据; //必须通过context.commit()触发某个mutations才行 context commit(‘add’) },1000) } } }) //触发Actions methods: { handle(){ //触发actions的第一种方式 this.$store.dispatch(‘addAsync’) } } 触发actions异步任务时携带参数 //定义 Action const store = new Vuex.Store({ //state 中存放的就是全局共享的数据 state: { count: 0 }, mutations:{ add(state,step){ //变更状态 state.count += step } }, actions:{ addAsync(context,step){ setTimeout( () => { //在 Actions中,不能直接修改state中的数据; //必须通过context.commit()触发某个mutations才行 context.commit(‘add’,step) },1000) } } }) //触发Actions methods: { handle(){ //触发actions的第一种方式 this.$store.dispatch(‘addAsync’,5) } } 第二种 import { mapActions } from ‘vuex’ 通过导入的mapActions函数,将需要的actions函数,映射为当前组件的methods函数 methods:{ …mapActions([‘addASync’,‘addNAsync’]) } Getter用于对store中的数据进行加工处理形成新的数据 //定义 Getter const store = new Vuex.Store({ state: { count: 0 }, getters: { showNum: state => { return ‘当前最新的数量是【’+ state.count +‘】’ } } }) 使用方式 this.$store .getters.名称 import { mapGetters } from ‘vuex’ computed:{ …mapGetters([‘showNum’]) } 当遇见大型项目时,数据量大,store就会显得很臃肿 为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割: 默认情况下,模块内部的 action 和 mutation 仍然是注册在全局命名空间的——这样使得多个模块能够对同一个 action 或 mutation 作出响应。 如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。要想打开git config 命令的帮助手册
不想查看完整的手册
获取Git仓库的两种方式
在现有目录中初始化仓库
工作区中文件的4种状态
检查文件的状态
以精简的方式显示文件状态
跟踪新文件
提交更新
对已提交的文件进行修改
暂存已修改的文件
提交已暂存的文件
撤销对文件的修改
向暂存区中一次性添加多个文件
取消暂存的文件
跳过使用暂存区域
移除文件
忽略文件
.gitignore的配置文件
glob模式
查看提交历史
回退到指定的版本
vuex
概述
使用vuex统一管理状态的好处
vuex基本使用
安装vuex依赖包
导入vuex包
创建store对象
vuex的核心概念
State
Mutations
Actions
Getter
Module
第二种方式
Actions
Getter
Module