谷粒商城项目学习笔记
一、项目架构图
1.1 项目微服务架构图
1.2 微服务划分图
二、环境搭建
2.1 安装linux虚拟机
VirtualBox+Vagrant安装虚拟机失败。使用VMWare
2.1.1 下载VirtualBox(开源)虚拟机
作用于VMWare一致
下载进行安装(需要开启CPU虚拟化)
2.1.2 下载&安装 Vagrant
进行快速创建虚拟机需要搭配Oracle VM VirtualBox进行使用
-
下载地址 https://www.vagrantup.com/downloads.html Vagrant 下载
-
下载完成后,重启系统,打开cmd测试是否安装成功,输入
vagrant
,出现指令 -
https://app.vagrantup.com/boxes/search Vagrant 官方镜像仓库
-
在镜像官网寻找镜像[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wLp9qlB8-1658967177213)(resource/image/image-20220722154742854.png)]
-
根据镜像名称进行安装
-
进入虚拟机要安装的文件夹打开cmd键入
vagrant init centos/7
最后一个值为镜像官网的镜像名称 -
文件夹生成一个Vagrantfile文件后,在cmd窗口键入
vagrant up
进行安装镜像,安装完成后VirtualBox会生成镜像系统 -
安装成功后在cmd中
vagrant ssh
进行连接,系统 root 用户的密码是vagrant -
修改IP
-
在本地Vagrantfile文件中
-
重启vagrant:
vagrant reload
-
-
允许账号密码登录
vi /etc/ssh/sshd_config 修改 PasswordAuthentication yes/no 重启服务 service sshd restart
2.2 Docker安装
虚拟化容器技术。Docker基于镜像,可以秒级启动各种容器。每一种容器都是一个完整的运行 环境,容器之间互相隔离。
-
删除docker
yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine
-
安装 Docker-CE
yum install -y yum-utils
-
设置下载地址
yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo
-
安装Docker
yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin
-
启动docker
systemctl start docker
-
查看docker版本
docker -v
-
设置docker自启动
systemctl enable docker
-
配置docker镜像加速(使用阿里云镜像加速器)
sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://afjpae5l.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo systemctl restart docker
2.2.1 docker简单使用
## 查看docker中的容器
docker images
## 查看docker中正在运行的容器
docker ps
## 进入容器 (一个容器相当于一个linux系统)
docker exec -it 容器id 或 容器names /bin/bash
## 退出容器
exit;
## 重启容器
docker restart 容器id 或 容器names
## 设置容器自动启动
docker update 容器名 --restart=always
2.3 Docker安装mysql
Docker镜像网站 https://hub.docker.com/
2.3.1 下载镜像文件
## 冒号后面可以跟dockerhub网站中的版本
docker pull mysql:5.7
2.3.2 创建实例并启动
docker run -p 3306:3306 --name mysql \
-v /mydata/mysql/log:/var/log/mysql \
-v /mydata/mysql/data:/var/lib/mysql \
-v /mydata/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
# 参数说明
# -p 3306:3306:将容器的 3306 端口映射到主机的 3306 端口
# --name 设置容器名字
# -v 目录挂载 将冒号后面(容器内部的文件)映射到linux,这样修改容器的文件不需要进入到docker
# -v /mydata/mysql/conf:/etc/mysql:将配置文件夹挂载到主机
# -v /mydata/mysql/log:/var/log/mysql:将日志文件夹挂载到主机
# -v /mydata/mysql/data:/var/lib/mysql/:将配置文件夹挂载到主机
# -e MYSQL_ROOT_PASSWORD=root:初始化 root 用户的密码
# -d 后台运行 mysql:5.7表示使用mysql:5.7启动容器
2.3.3 修改MySQL配置
因为设置了目录挂载,直接设置linux下的文件即可
vi /mydata/mysql/conf/my.cnf
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve
注意:解决 MySQL 连接慢的问题
在配置文件中加入如下,并重启 mysql [mysqld] skip-name-resolve 解释: skip-name-resolve:跳过域名解析
- 重启mysql容器
docker restart mysql
-
查看是否修改成功
-
进入mysql容器中
docker exec -it mysql /bin/bash cat /etc/mysql/my.cnf
-
-
设置root远程访问
docker exec -it mysql mysql -uroot -proot grant all privileges on *.* to 'root'@'%' identified by 'root' with grant option; flush privileges;
2.4 Docker安装Redis
-
下载容器
docker pull redis
-
创建实例并启动
# 创建redis挂载目录 mkdir -p /mydata/redis/conf touch /mydata/redis/conf/redis.conf # 创建实例并启动 docker run -p 6379:6379 --name redis \ -v /mydata/redis/data:/data \ -v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf \ -d redis redis-server /etc/redis/redis.conf # redis-server /etc/redis/redis.conf 以/etc/redis/redis.conf配置文件启动redis-server
-
修改redis配置(配置自己找)
https://raw.githubusercontent.com/antirez/redis/4.0/redis.conf
(需要关闭保护模式,并且注释bind)
vi /mydata/redis/conf/redis.conf # 重启docker容器 docker restart redis
-
测试redis
docker exec -it redis redis-cli
2.5 开发环境统一
2.5.1 Maven
-
windows配置阿里云镜像,在maven安装位置修改settings.xml文件
-
maven安装位置:D:\Software\Java\apache-maven-3.8.5\conf
<mirrors> <mirror> <id>nexus-aliyun</id> <mirrorOf>central</mirrorOf> <name>Nexus aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </mirror> </mirrors>
-
配置maven使用jdk1.8编译项目
<profiles> <profile> <id>jdk-1.8</id> <activation> <activeByDefault>true</activeByDefault> <jdk>1.8</jdk> </activation> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> </properties> </profile> </profiles>
-
在idea中配置maven
2.5.2 Idea & VsCode
-
idea 安装
lombok、mybatisx 插件
-
Vscode 安装
- 开发必备插件 Vetur —— 语法高亮、智能感知、
- Emmet 等 包含格式化功能, Alt+Shift+F (格式化全文),Ctrl+K Ctrl+F(格式化选中代码,两个 Ctrl 需要同时按着)
- EsLint —— 语法纠错
- Auto Close Tag —— 自动闭合 HTML/XML 标签
- Auto Rename Tag —— 自动完成另一侧标签的同步修改
- JavaScript(ES6) code snippets — — ES6 语 法 智 能 提 示 以 及 快 速 输 入 , 除 js 外 还 支 持.ts,.jsx,.tsx,.html,.vue,省去了配置其支持各种包含 js 代码文件的时间
- HTML CSS Support —— 让 html 标签上写 class 智能提示当前项目所支持的样式
- HTML Snippets —— html 快速自动补全
- Open in browser —— 浏览器快速打开
- Live Server —— 以内嵌服务器方式打开
- Chinese (Simplified) Language Pack for Visual Studio Code —— 中文语言包
2.5.3 安装Git
参考git笔记
2.5.4 创建项目
-
在码云创建项目
-
在IDEA进行Clone 链接
2.5.5 创建项目微服务
商品服务、仓储服务、订单服务、优惠券服务、用户服务
共同:
- web、openfeign
- 每一个服务,package名 **com.yl.gulimall.xxx(product/order/ware/coupon/member) **
- group:gulimall-XXX
- 提交到git(设置忽略文件)
2.5.6 创建数据库
先在虚拟机中设置MySQL和redis自启动
docker update redis --restart=always
docker update mysql --restart=always
创建数据库,导入sql
三、后端管理系统快速开发
3.1 下载人人开源管理系统及后端系统配置
我们使用人人开源管理系统进行快速开发
- 系统克隆地址https://gitee.com/renrenio[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NBgi1TPJ-1658967177219)(resource/image/image-20220722204258679.png)]
- 克隆完成之后,将
renren-fast
项目加入到我们的gulimall
父工程 - 将
renren-fast
中db文件夹的mysql.sql
文件运行到新的数据库gulimall_admin
- 将
renren-fast
中pom文件的parent
标签删除,并在父工程添加model
标签。 - 运行项目
3.2 前端工程的配置
3.2.1 环境搭建
-
安装node,参考其他笔记
-
安装依赖
# 在vsCode中打开控制台, npm install npm run dev
-
运行成功,出现登陆界面,使用admin admin进行登陆
3.3 逆向工程&使用
首先克隆renren-generator:https://gitee.com/renrenio/renren-generator
-
克隆完成后,将项目加入gulimall父工程,并将其添加为子工程
-
修改pom文件
<!-- 添加 --> <properties> <spring-boot.version>2.3.7.RELEASE</spring-boot.version> <spring-cloud.version>Hoxton.SR9</spring-cloud.version> </properties> <!-- 在dependency下面添加以下 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
3.3.1 修改逆向工程yaml文件
更改application.yml文件的mysql地址 将数据库改为 要逆向生成的数据库地址,并修改账号密码
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
#MySQL配置
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.219.121:3306/gulimall_pms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
3.3.2 修改template文件
修改逆向工程中resource下template中Controller.java.vm文件,将其中的@RequiresPermissions注解(Shrio安全配置)注释,暂时不用。然后运行项目
生成逆向工程文件,将文件中main文件夹替换微服务项目中的文件。
逆向生成的文件中依赖renren-fast中的文件,所以创建maven工程gulimall-common公共资源工程,并添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>gulimall</artifactId>
<groupId>cn.zcx.gulimall</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gulimall-common</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.14</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
</project>
-
将逆向工程所需要的公共依赖从renren-fast中复制到到common工程中
3.3.3整合mybatis-plus
-
在所有的微服务模块中引入common
-
common的pom文件中导入MySQL驱动
<!-- 导入mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
在product中配置yum文件
spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.219.121:3306/gulimall_pms
driver-class-name: com.mysql.jdbc.Driver
mybatis-plus:
# 映射文件
mapper-locations: classpath*:/mapper/**/*.xml
# 主键自增
global-config:
db-config:
id-type: auto
server:
port:
其他modeul同理
- 并且设置端口号(7000/8000/9000/10000/11000)
server:
port: 10000
四、分布式组件-SpringCloud Alibaba
4.1 Nacos[作为注册中心]
4.1.1 在common中导入版本控制(spring版本为2.1.18)
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
4.1.2 在common的pom中引入 Nacos Discover
<!--nacos-discovery 注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
Nacos 是阿里巴巴开源的一个更易于构建云原生应用的动态服务发现、配置管理和服务管理 平台。他是使用 java 编写。需要依赖 java 环境 Nacos 文档地址: https://nacos.io/zh-cn/docs/quick-start.html
- 1)下载 nacos-server https://github.com/alibaba/nacos/releases
- 2)启动 nacos-server
- 双击 bin 中的 startup.cmd 文件
- 访问 http://localhost:8848/nacos/
- 使用默认的 nacos/nacos 进行登录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ddJ2Ps1r-1658967177220)(images/image-20220726091423404.png)]
4.1.3 将微服务注册到 nacos
- 将 Nacos 服务器地址配置添加到文件 /src/main/resources/application.yml。
- 使用@EnableDiscoveryClient注释启用服务注册和发现。
注意:每一个应用都应该有名字,这样才能注册上去。
4.2 OpenFeign测试远程调用
4.2.1步骤
- 引入openfeign
- 编写一个接口,告诉springcloud这个借口需要调用远程服务
- 声明接口中的每一个方法都是调用的那个服务的那个请求(路径写全了)
- 开启远程调用功能
- 例子(member调用coupons)
- 在coupons中的controller
@RequestMapping("/member/list")
public R membercoupons() {
CouponEntity couponEntity = new CouponEntity();
couponEntity.setCouponName("满100减10");
return R.ok().put("coupons", Arrays.asList(couponEntity));
}
- 在member中创建feign包,编写接口
@FeignClient("gulimall-coupon")
public interface CouponFeignService {
@RequestMapping("/coupon/coupon/member/list")
public R membercoupons();
}
- 添加注解调用openfeign(记得打开nacos)
@EnableFeignClients(basePackages = "com.yl.gulimall.member.feign") //引入feifn所在的包
@SpringBootApplication
@EnableDiscoveryClient
public class GulimallMemberApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallMemberApplication.class, args);
}
}
localhost:8000/member/member/coupons 调用成功
4.3 Nacos[作为配置中心]
官方文档:
spring-cloud-alibaba/readme-zh.md at 2.2.x · alibaba/spring-cloud-alibaba · GitHub
4.3.1 在common的pom.xml 引入 Nacos Config Starter
<!-- 配置中心来做配置管理-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
4.3.2在应用的 /src/main/resources/bootstrap.properties 配 置文件中配置 Nacos
spring.application.name=gulimall-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
4.3.3在nacos中添加配置
在 nacos 中创建一个 应用名.properties 配置文件并编写
4.3.4在应用中使用@Value 和@RefreshSco
@RefreshScope //动态获取并刷新配置
@RestController
@RequestMapping("coupon/coupon")
public class CouponController {
@Autowired
private CouponService couponService;
@Value("${coupon.user.name}")
private String name;
@Value("${coupon.user.age}")
private Integer age;
@RequestMapping("/test")
public R test() {
return R.ok().put("name", name).put("age", age);
}
如果配置中心和当前应用的配置文件中配置了相同的项,优先使用配置中心的配置
4.4命名空间和配置分组
4.4.1命名空间
默认:public(保留空间):默认新增的所有配置都在public空间
1.开发、测试、生产:利用命名空间来做环境隔离
- 在bootstrap.properties配置上,需要使用哪个命名空间下的配置
spring.cloud.nacos.config.namespace=0706d5ad-15d3-4755-ac17-259f2c057c33
2.基于微服务进行隔离
- 每一个微服务之间互相隔离配置,每一个微服务都创建自己的命名空间,只加载自己命名空间下的所有配置
#每一个微服务之间互相隔离配置,每一个微服务都创建自己的命名空间,只加载自己命名空间下的所有配置
spring.cloud.nacos.config.namespace=00580b5a-a8c0-48b0-a0fc-4f032aa9fbf0
4.4.2配置集
所有的配置的集合
4.4.3配置集ID
类似于文件名 data ID
4.4.4配置分组
默认所有的配置集都属于DEFAULT_GROUP
- 每个微服务创建自己的命名空间,使用配置分组区分环境
4.5加载多配置集
- 微服务任何配置信息,任何配置文件都可以放在配置中心中
- 只需要在bootstrap.properties 说明加载配置中心中那些配置文件即可
- 以前springboot任何方法从配置文件中获取值,都能使用
- 配置中心有的有限使用配置中心中的
- bootstrap.properties
spring.application.name=gulimall-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
#每一个微服务之间互相隔离配置,每一个微服务都创建自己的命名空间,只加载自己命名空间下的所有配置
spring.cloud.nacos.config.namespace=00580b5a-a8c0-48b0-a0fc-4f032aa9fbf0
spring.cloud.nacos.config.group=dev
spring.cloud.nacos.config.extension-configs[0].data-id=datasource.yaml
spring.cloud.nacos.config.extension-configs[0].group=dev
spring.cloud.nacos.config.extension-configs[0].refresh=true
spring.cloud.nacos.config.extension-configs[1].data-id=mybatis.yaml
spring.cloud.nacos.config.extension-configs[1].group=dev
spring.cloud.nacos.config.extension-configs[1].refresh=true
spring.cloud.nacos.config.extension-configs[2].data-id=other.yaml
spring.cloud.nacos.config.extension-configs[2].group=dev
spring.cloud.nacos.config.extension-configs[2].refresh=true
- 在nacos上配置
mybatis.ymal
datasource.yaml
other.yaml
4.6 Gateway网关核心概念&原理
满足某些断言(predicates)就路由到指定的地址(uri),使用指定的过滤器(filter)
4.6.1 添加“gulimall-common”依赖和“spring-cloud-starter-gateway”依赖
<dependency>
<groupId>com.yl.gulimall</groupId>
<artifactId>gulimall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
4.6.2 网关相关配置(例子)(yml)
spring:
cloud:
gateway:
routes:
- id: test_route
uri: http://www.baidu.com
predicates:
- Query=url,baidu
- id: qq_route
uri: http://www.qq.com
predicates:
- Query=url,qq
- 各种 Predicates 同时存在于同一个路由时,请求必须同时满足所有的条件才被这个路 由匹配。
- 一个请求满足多个路由的谓词条件时,请求只会被首个成功匹配的路由转发
4.6.3 “GulimallGatewayApplication”类上加上“@EnableDiscoveryClient”注解
/*
* 1.开启服务注册发蔫(配置nacos的注册中心地址)
* */
@EnableDiscoveryClient
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class GulimallGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallGatewayApplication.class, args);
}
}
4.6.4在Nacos中创建“gateway”命名空间,同时在该命名空间中创建“gulimall-gateway.yml”
4.6.5 创建“bootstrap.properties”文件,添加如下配置,指明配置中心地址和所属命名空间
spring.application.name=gulimall-gateway
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
#每一个微服务之间互相隔离配置,每一个微服务都创建自己的命名空间,只加载自己命名空间下的所有配置
spring.cloud.nacos.config.namespace=27c870b8-3faa-47fe-9fc2-8b48c6d250cd
4.6.6 创建“application.properties”文件,指定服务名和注册中心地址
# 应用名称
spring.application.name=gulimall-gateway
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
server.port=88
4.6.7 启动“gulimall-gateway”
启动报错:
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
解决方法:在“com.bigdata.gulimall.gulimallgateway.GulimallGatewayApplication”中排除和数据源相关的配置
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
重新启动
五、ES6
1. let声明变量和const 声明常量(只读变量)
<script>
// var 声明的变量往往会越域
// let 声明的变量有严格局部作用域
{
var a = 1;
let b = 2;
}
console.log(a); // 1
console.log(b); // ReferenceError: b is not defined
// var 可以声明多次
// let 只能声明一次
var m = 1;
var m = 2;
let n = 3;
// let n = 4
console.log(m); // 2
console.log(n); // Identifier 'n' has already been declared
// var 会变量提升
// let 不存在变量提升
console.log(x); // undefined
var x = 10;
console.log(y); //ReferenceError: y is not defined
let y = 20;
// 1. 声明之后不允许改变
// 2. 一但声明必须初始化,否则会报错
const a = 1;
a = 3; //Uncaught TypeError: Assignment to constant variabl
</script>
2.解构表达式
- 数组解构
const [x, y, z] = arr;
- 对象解构
const { name,age,language } = person;
<!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>
<script>
// 1.数组解析
let arr = [1, 2, 3];
//以前我们想获取其中的值,只能通过角标。ES6 可以这样:
const [x, y, z] = arr; // x,y,z 将与 arr 中的每个位置对应来取值
// 然后打印
console.log(x, y, z);
const person = {
name: "jack",
age: 21,
language: ["java", "js", "css"],
};
// 2.对象解构
// 解构表达式获取值,将 person 里面每一个属性和左边对应赋值
const { name,age,language } = person;
// 等价于下面
// const name = person.name;
// const age = person.age;
// const language = person.language;
// 可以分别打印
console.log(name);
console.log(age);
console.log(language);
//扩展:如果想要将 name 的值赋值给其他变量,可以如下,nn 是新的变量名
// const { name: nn, age, language } = person;
// console.log(nn);
// console.log(age);
// console.log(language);
</script>
</body>
</html>
3.字符串扩展
ES6 为字符串扩展了几个新的 API:
includes()
:返回布尔值,表示是否找到了参数字符串。startsWith()
:返回布尔值,表示参数字符串是否在原字符串的头部。endsWith()
:返回布尔值,表示参数字符串是否在原字符串的尾部。
let str = "hello.vue";
console.log(str.startsWith("hello"));//true
console.log(str.endsWith(".vue"));//true
console.log(str.includes("e"));//true
console.log(str.includes("hello"));//true
4.字符串模板
模板字符串相当于加强版的字符串,用反引号 `,除了作为普通字符串,还可以用来定义多行 字符串,还可以在字符串中加入变量和表达式。
// 1、多行字符串
let ss = `
<div>
<span>hello world<span>
</div>
`
console.log(ss);
// 2、字符串插入变量和表达式。变量名写在 ${} 中,${} 中可以放入 JavaScript 表达式。
let name = "张三";
let age = 18;
let info = `我是${name},今年${age}了`;
console.log(info);
// 3、字符串中调用函数
function fun() {
return "这是一个函数";
}
let sss = `O(∩_∩)O 哈哈~,${fun()}`;
console.log(sss); // O(∩_∩)O 哈哈~,这是一个函数
5.函数优化
函数参数默认值 function add2(a, b = 1)
//在 ES6 以前,我们无法给一个函数参数设置默认值,只能采用变通写法:
function add(a, b) {
// 判断 b 是否为空,为空就给默认值 1
b = b || 1;
return a + b;
}
// 传一个参数
console.log(add(10));
//现在可以这么写:直接给参数写上默认值,没传就会自动使用默认值
function add2(a, b = 1) {
return a + b;
}
// 传一个参数
console.log(add2(10));
不定参数function fun(...values)
不定参数用来表示不确定参数个数,形如,…变量名,由…加上一个具名参数标识符组成。 具名参数只能放在参数列表的最后,并且有且只有一个不定参数
function fun(...values) {
console.log(values.length);
}
fun(1, 2); //2
fun(1, 2, 3, 4); //4
箭头函数
-
一个参数时:
var print = (obj) => console.log(obj);
-
//以前声明一个方法 // var print = function (obj) { // console.log(obj); // } // 可以简写为: var print = (obj) => console.log(obj); // 测试调用 print(100);
-
多个参数:
-
- 当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。
- 代码不止一行,可以用
{}
括起来
-
// 两个参数的情况: var sum = function (a, b) { return a + b; }; // 简写为: //当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。 var sum2 = (a, b) => a + b; //测试调用 console.log(sum2(10, 10)); //20 // 代码不止一行,可以用`{}`括起来 var sum3 = (a, b) => { c = a + b; return c; }; //测试调用 console.log(sum3(10, 20)); //3
箭头函数结合解构表达式
-
var hello2 = ({ name }) => {
console.log(“hello,” + name);
}; -
//需求,声明一个对象,hello 方法需要对象的个别属性 //以前的方式: const person = { name: "jack", age: 21, language: ["java", "js", "css"], }; function hello(person) { console.log("hello," + person.name); } //现在的方式 var hello2 = ({ name }) => { console.log("hello," + name); }; //测试 hello2(person);
6.对象优化
-
新增的api
-
ES6 给 Object 拓展了许多新的方法,如:
- keys(obj):获取对象的所有 key 形成的数组
- values(obj):获取对象的所有 value 形成的数组
- entries(obj):获取对象的所有 key 和 value 形成的二维数组。格式:
[[k1,v1],[k2,v2],...]
- assign(dest, …src) :将多个 src 对象的值 拷贝到 dest 中。(第一层为深拷贝,第二层为浅拷贝)
const person = {
name: "jack",
age: 21,
language: ["java", "js", "css"],
};
console.log(Object.keys(person)); //["name", "age", "language"]
console.log(Object.values(person)); //["jack", 21, Array(3)]
console.log(Object.entries(person)); //[Array(2), Array(2), Array(2)]
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
//Object.assign 方法的第一个参数是目标对象,后面的参数都是源对象。
Object.assign(target, source1, source2);
console.log(target); //{a: 1, b: 2, c: 3
-
声明对象简写
-
ES6:属性名和属性值变量名一样,可以省略
const age = 23; const name = "张三"; // 传统 const person1 = { age: age, name: name }; console.log(person1); // ES6:属性名和属性值变量名一样,可以省略 const person2 = { age, name }; console.log(person2); //{age: 23,
-
对象的函数属性简写
-
箭头函数版:拿不到 this (对象.属性)
eat2: (food) => console.log(person3.name + “在吃” + food),
let person3 = {
name: "jack",
// 以前:
eat: function (food) {
console.log(this.name + "在吃" + food);
},
// 箭头函数版:这里拿不到 this
eat2: (food) => console.log(person3.name + "在吃" + food),
// 简写版:
eat3(food) {
console.log(this.name + "在吃" + food);
},
};
person3.eat("苹果");
person3.eat2("香蕉");
person3.eat3("桃子");
-
对象拓展运算符
-
拓展运算符(…)用于取出参数对象所有可遍历属性然后拷贝到当前对象
// 1、拷贝对象(深拷贝)
let person4 = { name1: "Amy", age1: 15 };
let someone = { ...person4 };
console.log(someone); //{name: "Amy", age: 15}
// 2、合并对象
let age1 = { age1: 15 };
let name1= { name1: "Amy" };
let person5 = { ...age1, ...name1 }; //如果两个对象的字段名重复,后面对象字段值会覆盖前面对象的字段值;
console.log(person5); //{name1: 'Amy', age1: 15}
6. map 和 reduce
数组中新增了 map 和 reduce 方法
map
map():接收一个函数,将原数组中的所有元素用这个函数处理后放入新数组返回
let arr = ['1', '20', '-5', '3'];
console.log(arr)
arr = arr.map(s => s*2);
console.log(arr)
reduce
语法:
arr.reduce(callback,[initialValue])
reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元 素,接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。
callback (执行数组中每个值的函数,包含四个参数)
1、previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
2、currentValue (数组中当前被处理的元素)
3、index (当前元素在数组中的索引)
4、array (调用 reduce 的数组) initialValue (作为第一次调用 callback)
- 没有初始值:
const arr = [1, 20, -5, 3];
//没有初始值:
let array = arr.reduce((a, b) => {
console.log("上次处理后a:" + a);
console.log("当前处理b:" + b);
return a + b;
});
console.log("最后结果" + array); //19
// 简写
console.log(arr.reduce((a, b) => a + b)); //19
console.log(arr.reduce((a, b) => a * b)); //-300
- 指定初始值:
//指定初始值:
let array1 = arr.reduce((a, b) => {
console.log("上次处理后a:" + a);
console.log("当前处理b:" + b);
return a + b;
}, 2);
console.log("array1:" + array1); //21
// 简写
console.log(arr.reduce((a, b) => a + b, 2)); //21
console.log(arr.reduce((a, b) => a * b, 0)); //-0
7. Promise
- Promise 语法
const promise = new Promise((resolve, reject) =>{
// 执行异步操作
if (/* 异步操作成功 */) {
resolve(value);// 调用 resolve,代表 Promise 将返回成功的结果
} else {
reject(error);// 调用 reject,代表 Promise 会返回失败结果
}
});
- 处理异步结果
promise.then(function (value) {
// 异步执行成功后的回调
}).catch(function (error) {
// 异步执行失败后的回调
})
- 优化处理
<script src="js/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
let get = function (url, data) {
// 实际开发中会单独放到 common.js 中
return new Promise((resolve, reject) => {
$.ajax({
url: url,
data: data,
success(data) {
resolve(data);
},
error(error) {
reject(error);
},
});
});
};
// 使用封装的 get 方法,实现查询分数
get("mock/user.json")
.then((data) => {
console.log("查询用户:", data);
return get(`mock/user_corse_${data.id}.json`);
})
.then((data) => {
console.log("查询到课程:", data);
return get(`mock/corse_score_${data.id}.json`);
})
.then((data) => {
console.log("查询到分数:", data);
})
.catch((error) => {
console.log("出现异常了:" + error);
});
</script>
8.模块化
模块化就是把代码进行拆分,方便重复利用。类似 java 中的导包:要使用一个包,必须先 导包。而 JS 中没有包的概念,换来的是 模块。
模块功能主要由两个命令构成:
export
和import
。
export
命令用于规定模块的对外接口。
import
命令用于导入其他模块提供的功能。
-
export
不仅可以导出对象,一切 JS 变量都可以导出。比如:基本类型变量、函数、数组、 对象。export const util = { sum(a, b) { return a + b; }, };
-
省略名称
上面的导出代码中,都明确指定了导出的变量名,这样其它人在导入使用时就必须准确写出 变量名,否则就会出错。 因此 js 提供了
default
关键字,可以对导出的变量名进行省略
// 无需声明对象的名字
export default {
sum(a, b) {
return a + b;
},
};
这样,当使用者导入时,可以任意起名字
-
使用
export
命令定义了模块的对外接口以后,其他 JS 文件就可以通过import
命令加载这 个模块。 -
import util from "hello.js"; // 调用 util 中的属性 util.sum(1, 2); import {name, age} from 'user.js' onsole.log(name)
六、Vue
1. MVVM 思想
- M:即 Model,模型,包括数据和一些基本操作
- V:即 View,视图,页面渲染结果
- VM:即 View-Model,模型与视图间的双向操作(无需开发人员干涉)
2.创建vue项目
//初始化
cnpm install -y
//安装vue
cnpm install vue@2
3.事件处理
-
v-xx:指令
1、创建vue实例,关联页面的模板,将自己的数据(data)渲染到关联的模板,响应式的
2、指令来简化对dom的一些操作。
3、声明方法来做更复杂的操作。methods里面可以封装方法。 -
在VUE中el,data和vue的作用:
-
el:用来绑定数据;
-
data:用来封装数据;
-
methods:用来封装方法,并且能够封装多个方法,如何上面封装了cancell和hello方法。
-
-
“v-html”不会对于HTML标签进行转义,而是直接在浏览器上显示data所设置的内容;而“ v-text”会对html标签进行转义
-
{{msg}} :称为差值表达式,它必须要写在Html表达式,可以完成数学运算和方法调用
双向绑定
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>我是{{name}}</h1>
<button @click="num++">点赞</button>
<button @click="cancle">取消</button>
<h1>点赞次数:{{num}}</h1>
<a :href="link">百度</a>
<div>
精通的语言:<br>
<input type="checkbox" v-model="language" value="Java" /> java<br />
<input type="checkbox" v-model="language" value="PHP" /> PHP<br />
<input type="checkbox" v-model="language" value="Python" /> Python<br />
选中了 {{language.join(",")}}
</div>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
name: "大漂亮",
num: 0,
link: "http://www.baidu.com",
language:[]
},
methods: {
cancle() {
this.num = 0;
},
},
});
</script>
</body>
</html>
v-on为按钮绑定事件
v-on:click也可以写作@click
事件的冒泡:
<!-- 事件修饰符 -->
<div style="border: 1px solid red;padding: 20px;" v-on:click="hello">
大div
<div style="border: 1px solid blue;padding: 20px;" @click="hello">
小div <br />
<a href="http://www.baidu.com" @click.prevent="hello">去百度</a>
</div>
</div>
上面的这两个嵌套div中,如果点击了内层的div,则外层的div也会被触发;这种问题可以事件修饰符来完成:
<!-- 事件修饰符 -->
<div style="border: 1px solid red;padding: 20px;" v-on:click.once="hello">
大div
<div style="border: 1px solid blue;padding: 20px;" @click.stop="hello">
小div <br />
<a href="http://www.baidu.com" @click.prevent.stop="hello">去百度</a>
<!--这里禁止了超链接的点击跳转操作,并且只会触发当前对象的操作-->
</div>
</div>
- 关于事件修饰符:
为了解决这个问题,Vue.js 为 v-on
提供了事件修饰符。修饰符是由点开头的指令后缀来 表示的。
.stop
:阻止事件冒泡到父元素
.prevent
:阻止默认事件发生
.capture
:使用事件捕获模式
.self
:只有元素自身触发事件才执行。(冒泡或捕获的都不执行)
.once
:只执行一次
- 按键修饰符:
全部的按键别名:
.enter
.tab
.delete
(捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
- 组合按钮 可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
.ctrl
.alt
.shift
v-for遍历循环
遍历的时候都加上:key来区分不同数据,提高vue渲染效率
<ul>
<li
v-for="(user,index) in users"
:key="user.name"
v-if="user.gender == '女'"
>
<!-- 1、显示user信息:v-for="item in items" -->
当前索引:{{index}} ==> {{user.name}} ==> {{user.gender}}
==>{{user.age}} <br />
<!-- 2、获取数组下标:v-for="(item,index) in items" -->
<!-- 3、遍历对象:
v-for="value in object"
v-for="(value,key) in object"
v-for="(value,key,index) in object"
-->
对象信息:
<span v-for="(v,k,i) in user">{{k}}=={{v}}=={{i}};</span>
<!-- 4、遍历的时候都加上:key来区分不同数据,提高vue渲染效率 -->
</li>
</ul>
4.计算属性和侦听器
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<ul>
<li>
西游记:价格{{xyjPrice}},数量:
<input type="number" v-model="xyjNum" />
</li>
<li>
水浒传:价格{{shzPrice}},数量:
<input type="number" v-model="shzNum" />
</li>
<li>总价:{{totalPrice}}</li>
{{msg}}
</ul>
</div>
<script type="text/javascript">
let app = new Vue({
el: "#app",
data: {
xyjPrice: 55.55,
shzPrice: 66.66,
xyjNum: 1,
shzNum: 1,
msg:""
},
// 计算属性
computed: {
totalPrice() {
return this.xyjPrice * this.xyjNum + this.shzPrice * this.shzNum;
},
},
// 监听属性
watch: {
xyjNum(newVal, oldVal) {
if (newVal >= 3) {
this.msg = "西游记没有更多库存了";
this.xyjNum = 3;
} else {
this.msg = "";
}
},
},
});
</script>
</body>
</html>
5. 过滤器(filters)
- | 管道符号:表示使用后面的过滤器处理前面的数据
<!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>
<script src="./node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item, index) in items" :key="index">
id:{{item.id}};姓名:{{item.name}};性别:{{item.gender | sexFilter}}
全局过滤器:{{item.gender | capitalize}}
</li>
</ul>
</div>
<script>
// 在创建 Vue 实例之前全局定义过滤器
Vue.filter("capitalize", function (val) {
if (val == 1) {
return "男";
} else {
return "女";
}
});
let vm = new Vue({
el: "#app",
data: {
items: [
{ id: 1, name: "zs", gender: 1 },
{ id: 2, name: "lisi", gender: 0 },
],
},
// 局部过滤器
filters: {
sexFilter(val) {
if (val == 1) {
return "男";
} else {
return "女";
}
},
},
});
</script>
</body>
</html>
6.组件化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<button v-on:click="count++">我被点击了 {{count}} 次</button>
全局组件<counter></counter>
<counter></counter>
<!-- 使用所定义的组件button-counter -->
局部组件<button-counter></button-counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
//1、全局声明注册一个组件
Vue.component("counter", {
template: `<button v-on:click="count++">我被点击了 {{count}} 次</button>`,
data() {
return {
count: 1
}
}
});
//2、局部声明一个组件
const buttonCounter = {
template: `<button v-on:click="count++">我被点击了 {{count}} 次~~~</button>`,
data() {
return {
count: 1
}
}
};
new Vue({
el: "#app",
data: {
count: 1
},
components: {
//声明所定义的局部组件
'button-counter': buttonCounter
}
})
</script>
</body>
</html>
7.生命周期钩子函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<span id="num">{{num}}</span>
<button @click="num++">赞!</button>
<h2>{{name}},有{{num}}个人点赞</h2>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
name: "张三",
num: 100
},
methods: {
show() {
return this.name;
},
add() {
this.num++;
}
},
beforeCreate() {
console.log("=========beforeCreate=============");
console.log("数据模型未加载:" + this.name, this.num);
console.log("方法未加载:" + this.show());
console.log("html模板未加载:" + document.getElementById("num"));
},
created: function () {
console.log("=========created=============");
console.log("数据模型已加载:" + this.name, this.num);
console.log("方法已加载:" + this.show());
console.log("html模板已加载:" + document.getElementById("num"));
console.log("html模板未渲染:" + document.getElementById("num").innerText);
},
beforeMount() {
console.log("=========beforeMount=============");
console.log("html模板未渲染:" + document.getElementById("num").innerText);
},
mounted() {
console.log("=========mounted=============");
console.log("html模板已渲染:" + document.getElementById("num").innerText);
},
beforeUpdate() {
console.log("=========beforeUpdate=============");
console.log("数据模型已更新:" + this.num);
console.log("html模板未更新:" + document.getElementById("num").innerText);
},
updated() {
console.log("=========updated=============");
console.log("数据模型已更新:" + this.num);
console.log("html模板已更新:" + document.getElementById("num").innerText);
}
});
</script>
</body>
</html>
8.脚手架
1、npm install webpack -g
全局安装 webpack
2、npm install -g @vue/cli-init
全局安装 vue 脚手架
3、初始化 vue 项目; vue init webpack appname
vue 脚手架使用 webpack 模板初始化一个 appname 项目
4、启动 vue 项目; 项目的 package.json 中有 scripts,代表我们能运行的命令
-
npm start = npm run dev
启动项目 -
npm run build
将项目打包
vue模板
文件–>首选项–>用户代码片段–>点击新建代码片段–取名 vue.json 确定
{
"生成 vue 模板": {
"prefix": "vue",
"body": [
"<template>",
"<div></div>",
"</template>",
"",
"<script>",
"//这里可以导入其他文件(比如:组件,工具 js,第三方插件 js,json文件,图片文件等等)",
"//例如:import 《组件名称》 from '《组件路径》';",
"",
"export default {",
"//import 引入的组件需要注入到对象中才能使用",
"components: {},",
"props: {},",
"data() {",
"//这里存放数据",
"return {",
"",
"};",
"},",
"//计算属性 类似于 data 概念",
"computed: {},",
"//监控 data 中的数据变化",
"watch: {},",
"//方法集合",
"methods: {",
"",
"},",
"//生命周期 - 创建完成(可以访问当前 this 实例)",
"created() {",
"",
"},",
"//生命周期 - 挂载完成(可以访问 DOM 元素)",
"mounted() {",
"",
"},",
"beforeCreate() {}, //生命周期 - 创建之前",
"beforeMount() {}, //生命周期 - 挂载之前",
"beforeUpdate() {}, //生命周期 - 更新之前",
"updated() {}, //生命周期 - 更新之后",
"beforeDestroy() {}, //生命周期 - 销毁之前",
"destroyed() {}, //生命周期 - 销毁完成",
"activated() {}, //如果页面有 keep-alive 缓存功能,这个函数会触发",
"}",
"</script>",
"<style lang='scss' scoped>",
"//@import url($3); 引入公共 css 类",
"$4",
"</style>"
],
"description": "生成 vue 模板"
}
}
9.导入 element-ui 快速开发
1、安装 element-ui: npm i element-ui
2、在 main.js 中引入 element-ui 就可以全局使用了。
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
3、将 App.vue 改为 element-ui 中的后台布局
4、添加测试路由、组件,测试跳转逻辑