0%

application.properties基本使用

application.properties的使用,主要用来配置数据库连接、日志相关配置等。

应用配置文件位置

  • spring会从classpath下的/config目录或者classpath的根目录查找application.propertiesapplication.yml
  • /config优先于classpath根目录
  • @PropertySource这个注解可以指定具体的属性配置文件,优先级比较低。
  • 相同优先级位置同时有application.propertiesapplication.yml,那么application.yml里面的属性就会覆盖application.properties里的属性。

应用配置文件(.properties或.yml)

在配置文件中直接写:

1
2
name=MaxZhao
server.port=8080

.yml格式的配置文件如:

1
2
3
4
5
name: MaxZhao
server:
port: 8080
servlet:
context-path: /gtboot

当有前缀的情况下,使用.yml格式的配置文件更简单。详情请查看*
这里*

注意:使用.yml时,属性名的值和冒号中间必须有空格,如name: MaxZhao正确,name:MaxZhao就是错的。


application.properties其它用法

自定义属性加载

通常我们需要配置文件来定义一些自己的属性,比如:

1
2
my.name=MaxZhao
my.sex=1

在代码中我们可以这样引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

@Component
public class MyInfoProperties {
@Value("${my.name}")
private String name;
@Value("${my.title}")
private String sex;

// 省略getter和setter
}

// 或者
@ConfigurationProperties(prefix = "my")
public class MyInfoProperties {
private String name;
private String sex;

// 省略getter和setter
}

然后其它类中引用

1
2
3
4
public class MyInfoPropertiesDemo {
@Autowired
private MyInfoProperties myInfoProperties;
}

List、Map 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14

@ConfigurationProperties(prefix = "my")
public class MyInfoProperties {
private String name;
private String sex;
private MyInfoProperties info;
private List<MyInfoProperties> infos;
private List<String> names;
private List<String> names2;
private Map<String, MyInfoProperties> infoMap;
private Map<String, String> nameMap;
private Map<String, String> nameMap2;
private Map<String, String> nameMap3;
}

下面展示一下 yml 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
my:
name: MaxZhao
sex: 1
info:
name: 用户1
sex:
info2: { name: 用户1, sex: }
infos:
- name: 用户1
sex:
- name: 用户2
sex:
names:
- 用户1
- 用户2
names2: 用户1,用户2
infoMap:
用户1:
name: 用户1
ex:
用户2:
name: 用户2
ex:
nameMap:
用户1:
用户2:
nameMap2[用户1]:
nameMap2[用户2]:
nameMap3: { 用户1: ,用户2: }

通过命令行设置属性值

java -jar xxx.jar --server.port=8888,这三通过使用—server.port属性来设置xxx.jar应用的端口为8888。
jjava -jar xxx.jar --server.port=8888j命令,等价于我们在application.properties中添加属性server.port=8888

可以用SpringApplication.setAddCommandLineProperties(false)禁用命令行设置属性值。

通过代码设置属性值

1
2
3
4
5
6
7
8
9
10
public class MyInfoPropertiesDemo {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
Map<String, Object> defaultMap = new HashMap<String, Object>();
defaultMap.put("name", "MaxZhao");
//还可以是Properties对象
application.setDefaultProperties(defaultMap);
application.run(args);
}
}

多环境配置

通常同一套程序会被应用和安装到几个不同的环境,比如:开发、测试、生产等。其中每个环境的数据库地址、服务器端口等等配置都会不同。 在Spring
Boot中多环境配置文件名需要满足application-{profile}.properties的格式,其中{profile}对应你的环境标识,比如:

  • application-dev.properties:开发环境
  • application-test.properties:测试环境
  • application-prod.properties:生产环境

至于哪个具体的配置文件会被加载,需要在 application.properties 文件中通过 spring.profiles.active属性来设置,其值对应 {profile}值。

如:spring.profiles.active=test就会加载application-test.properties配置文件内容
可以用命令行执行java -jar xxx.jar --spring.profiles.active=test

按照上面的实验,可以如下总结多环境的配置思路:

application.properties中配置通用内容,并设置spring.profiles.active=dev,以开发环境为默认配置。
application-{profile}.properties中配置各个环境不同的内容。 通过命令行方式去激活不同环境的配置。

多环境配置的另一种方式pom.xml

application.yml中配置:

1
2
3
4
spring:
profiles:
# 获取 pom文件的内容,需要加引号,单双引号都可以
active: '@spring.active@'

pom.xml中配置:

1
2
3
4
5
6
7
8
9

<profiles>
<profile>
<id>dev</id>
<properties>
<spring.active>dev</spring.active>
</properties>
</profile>
</profiles>

最后在mavenProfiles中选择dev,运行就可以了,这样就能很好的区分开发环境和上线环境。

随机数的使用

application.properties中:

1
2
3
4
5
6
7
8
9
10
# 随机字符串
random.value=${random.value}
# 随机int
random.number=${random.int}
# 随机long
random.bignumber=${random.long}
# 10以内的随机数
random.test1=${random.int(10)}
# 10-20的随机数
random.test2=${random.int[10,20]}

参数间的引用

application.properties中:

1
2
3
my.name=MaxZhao
my.sex=1
my.des=My name is ${MaxZhao}.

通过属性占位符还能缩短命令参数

例如修改web默认端口需要使用--server.port=9090方式,如果在配置中写上:

1
server.port=${port:8080}

那么就可以使用更短的--port=9090,当不提供该参数的时候使用默认值8080

Java系统属性

注意Java系统属性位置java -Dname="MaxZhao" -jar xxx.jar,可以配置的属性都是一样的,优先级不同。

例如java -Dname="MaxZhao" -jar xxx.jar --name="Spring!"name值为Spring!

属性名匹配规则 例如有如下配置对象:

1
2
3
4
5
6

@Component
@ConfigurationProperties(prefix = "person")
public class ConnectionSettings {
private String firstName;
}

firstName可以使用的属性名如下:

  • person.firstName,标准的驼峰式命名
  • person.first-name,虚线(-)分割方式,推荐在.properties和.yml配置文件中使用
  • PERSON_FIRST_NAME,大写下划线形式,建议在系统环境变量中使用

devtools 配置

1
2
3
4
5
spring.devtools.restart.enabled=true
# 设置重启目录
spring.devtools.restart.additional-paths=src/main/java
# 排除不需要重启的目录,默认值在spring-boot-devtools/2.1.2.RELEASE/spring-boot-devtools-2.1.2.RELEASE-sources.jar!/org/springframework/boot/devtools/autoconfigure/DevToolsProperties.java
#spring.devtools.restart.exclude=

MySQL8 配置

1
2
3
4
5
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/maxzhao_ittest?charset=utf8mb4&useSSL=false
spring.datasource.username=maxzhao
spring.datasource.password=maxzhao
#com.mysql.cj.PerConnectionLRUFactory

Redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Redis 数据库索引
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=maxzhao
# 连接池最大连接数
spring.redis.jedis.pool.max-active=1000
# 连接池最大阻塞等待时间,负值没有限制
spring.redis.jedis.pool.max-wait=-1
# 连接池中最大空闲连接
spring.redis.jedis.pool.max-idle=10
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=2
# 超时时间 毫秒
spring.redis.timeout=1000

上传文件大小限制配置

1
2
3
4
5
6
7
spring:
application:
name: maxzhao
servlet:
multipart:
max-file-size: 100MB # MB一定要大写
max-request-size: 100MB

jackson时区配置

1
2
3
4
5
6
7
8
# 方法1
spring:
application:
name: maxzhao
jackson:
date-format: yyyy-MM-dd
time-zone: GMT+8
# 方法2 在pojo上添加JonFormat

日志

1
2
3
4
5
6
7
8
9
10
11
logging:
config: classpath:log-boot.xml
level:
# 全部路径 TARCE < DEBUG < INFO < WARN < ERROR < FATAL
root: info
# 指定包
com:
jackie:
springbootdemo:
config: debug
# file:

RabbitMQ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
#支持发布确认
publisher-confirms: true
#支持发布返回
publisher-returns: true
# 默认 /
virtual-host: vhost
listener:
simple:
#采用手动应答
acknowledge-mode: manual
#指定最小的消费者数量
concurrency: 1
#指定最大的消费者数量
max-concurrency: 1
retry:
#是否支持重试
enabled: true

本文地址:SpringBoot的配置文件及获取配置信息

本文地址: https://github.com/maxzhao-it/blog/post/56669/

前言

webpack开发 vue的过程中, 有些系统级的配置需要修改, 这时候就需要了解一些系统配置.

Build/ 目录下

config/ 目录下

index.js

这里需要修改 dev 模式下的跨域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
proxyTable: {//跨域 本地服务器 --》 代理 --》目标服务器 --》拿到数据后通过代理伪装成本地服务请求的返回值 ---》然后浏览器就顺利收到了我们想要的数据
//意思是 把 本地localhost:XXX/api/XX 代理到 localhost:8062/gtboot/XX
'/api': {
target: 'http://localhost:8062', //目标接口域名
changeOrigin: true,//是否跨域
pathRewrite: {
'^/api': '/gtboot' //重写接口
},
},
'/jwt': {
target: 'http://localhost:8060',
changeOrigin: true,//是否跨域
pathRewrite: {
'^/jwt': '/gtboot'
},
}
},

本文地址: https://github.com/maxzhao-it/blog/post/47805/

简介

强烈推荐官方文档:很详细了。

我这里主要是以linux下的使用。

安装

我这里以我的安装路径为准/home/maxzhao/soft/

1.预备环境准备

Nacos 依赖 Java
环境来运行。如果您是从代码开始构建并运行Nacos,还需要为此配置 Maven环境,请确保是在以下版本环境中安装使用:

  1. 64 bit OS,支持 Linux/Unix/Mac/Windows,推荐选用 Linux/Unix/Mac。
  2. 64 bit JDK 1.8+;下载
    & 配置
  3. Maven 3.2.x+;下载 & 配置

2.从 Github 上下载源码方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cd /home/maxzhao/soft/
git clone https://github.com/alibaba/nacos.git
cd nacos/
# 会安装一个稳定的版本
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
ls -al distribution/target/
# drwxr-xr-x 2 maxzhao maxzhao 4096 4月 20 10:16 archive-tmp
# drwxr-xr-x 3 maxzhao maxzhao 4096 4月 20 10:16 nacos-server-1.0.0
# -rw-r--r-- 1 maxzhao maxzhao 42946896 4月 20 10:16 nacos-server-1.0.0.tar.gz
# -rw-r--r-- 1 maxzhao maxzhao 42948559 4月 20 10:16 nacos-server-1.0.0.zip
# change the $version to your actual path
# cd distribution/target/nacos-server-$version/nacos/bin
cd distribution/target/nacos-server-1.4.2-SNAPSHOT/nacos/bin

3.启动服务器

Linux/Unix/Mac

启动命令(standalone代表着单机模式运行,非集群模式):

1
sh startup.sh -m standalone

创建快捷启动方式

添加脚本环境变量路径

1
vim /etc/profile

在其他 append_path 添加

1
append_path '/home/maxzhao/script'

创建脚本

1
vim ~/script/gt-nacos-start 

添加 nacos 启动脚本,默认就是后台执行的

1
2
#!/bin/bash
sh /home/maxzhao/soft/nacos/distribution/target/nacos-server-1.4.2-SNAPSHOT/nacos/bin/startup.sh -m standalone

查看当前是否执行

1
jps -mV |grep nacos

关闭

1
sh /home/maxzhao/soft/nacos/distribution/target/nacos-server-1.4.2-SNAPSHOT/nacos/bin/shutdown.sh

4.服务注册&发现和配置管理——测试

服务注册

1
curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080'

服务发现

1
curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instances?serviceName=nacos.naming.serviceName'

发布配置

1
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld"

获取配置

1
curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"

5.关闭服务器

Linux/Unix/Mac

1
sh shutdown.sh

配置

端口配置

  • 配置在/bin/startup.sh(linux下),在set "JAVA_OPT=%JAVA_OPT%添加代码:set "JAVA_OPT=%JAVA_OPT% --server.port=8448

  • /home/maxzhao/soft/nacos/distribution/target/nacos-server-1.0.0/nacos/conf/application.properties
    配置文件中添加:server.port=8448

也可以在该文件下指定数据源,方法和springboot中配置一样(单机模式模式下默认连接的是javaDB),该文件夹下 nacos-logback.xml自然是修改nacos日志输出规则的。

如果是0.3.0+版本 启动后访问下面这个地址:http://127.0.0.1:8848/nacos/index.html

默认账号:nacos

默认密码:nacos

SpringCloud中的配置

resource目录下加入 bootstrap.properties文件 并添加配置中心相关信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#服务名
spring.application.name=nacos-config-example
#指定开发环境
spring.profiles.active=dev
#服务器地址
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
#默认为Public命名空间,可以省略不写
spring.cloud.nacos.config.namespace=Public
# 新建命名空间之后则为命名空间的ID
spring.cloud.nacos.config.namespace=
49d37ddb-3750-4b0e-afaf-c1494c4769dc
#指定配置群组 --如果是Public命名空间 则可以省略群组配置
spring.cloud.nacos.config.group=DEFAULT_GROUP
#文件名 -- 如果没有配置则默认为 ${spring.appliction.name}
spring.cloud.nacos.config.prefix=member
#指定文件后缀
spring.cloud.nacos.config.file-extension=yaml

本文地址: https://github.com/maxzhao-it/blog/post/53052/

简介

强烈推荐官方文档:很详细了。

nacos安装需要与使用的代码框架版本匹配

我这里主要是以linux下的使用。

安装

我这里以我的安装路径为准/home/maxzhao/soft/

1.预备环境准备

Nacos 依赖 Java
环境来运行。如果您是从代码开始构建并运行Nacos,还需要为此配置 Maven环境,请确保是在以下版本环境中安装使用:

  1. 64 bit OS,支持 Linux/Unix/Mac/Windows,推荐选用 Linux/Unix/Mac。
  2. 64 bit JDK 1.8+;下载 & 配置
  3. Maven 3.2.x+;下载 & 配置

2.从 Github 上下载源码方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cd /home/maxzhao/soft/
git clone https://github.com/alibaba/nacos.git
cd nacos/
# 会安装一个稳定的版本
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
ls -al distribution/target/
# drwxr-xr-x 2 maxzhao maxzhao 4096 4月 20 10:16 archive-tmp
# drwxr-xr-x 3 maxzhao maxzhao 4096 4月 20 10:16 nacos-server-1.0.0
# -rw-r--r-- 1 maxzhao maxzhao 42946896 4月 20 10:16 nacos-server-1.0.0.tar.gz
# -rw-r--r-- 1 maxzhao maxzhao 42948559 4月 20 10:16 nacos-server-1.0.0.zip
# change the $version to your actual path
# cd distribution/target/nacos-server-$version/nacos/bin
cd distribution/target/nacos-server-1.4.2-SNAPSHOT/nacos/bin

3.启动服务器

Linux/Unix/Mac

启动命令(standalone代表着单机模式运行,非集群模式):

1
sh startup.sh -m standalone

创建快捷启动方式

添加脚本环境变量路径

1
vim /etc/profile

在其他 append_path 添加

1
append_path '/home/maxzhao/script'

创建脚本

1
vim ~/script/gt-nacos-start 

添加 nacos 启动脚本,默认就是后台执行的

1
2
#!/bin/bash
sh /home/maxzhao/soft/nacos/distribution/target/nacos-server-1.4.2-SNAPSHOT/nacos/bin/startup.sh -m standalone

查看当前是否执行

1
jps -mV |grep nacos

关闭

1
sh /home/maxzhao/soft/nacos/distribution/target/nacos-server-1.4.2-SNAPSHOT/nacos/bin/shutdown.sh

4.服务注册&发现和配置管理——测试

服务注册

1
curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080'

服务发现

1
curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instances?serviceName=nacos.naming.serviceName'

发布配置

1
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld"

获取配置

1
curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"

5.关闭服务器

Linux/Unix/Mac

1
sh shutdown.sh

配置

端口配置

  • 配置在/bin/startup.sh(linux下),在set "JAVA_OPT=%JAVA_OPT%添加代码:set "JAVA_OPT=%JAVA_OPT% --server.port=8448

  • /home/maxzhao/soft/nacos/distribution/target/nacos-server-1.0.0/nacos/conf/application.properties
    配置文件中添加:server.port=8448

也可以在该文件下指定数据源,方法和springboot中配置一样(单机模式模式下默认连接的是javaDB),该文件夹下 nacos-logback.xml自然是修改nacos日志输出规则的。

如果是0.3.0+版本 启动后访问下面这个地址:http://127.0.0.1:8848/nacos/index.html

默认账号:nacos

默认密码:nacos

本文地址: https://github.com/maxzhao-it/blog/post/19052/

安装环境

1
2
3
4
sudo pacman -S npm
sudo npm install --registry=https://registry.npmmirror.com -g vue vue-cli
# 前段打包工具
sudo npm i --registry=https://registry.npmmirror.com -g webpack webpack-cli

由于 npm 安装速度慢,本教程使用了淘宝的镜像及其命令 cnpm,安装使用介绍参照:使用淘宝 NPM 镜像

vue-cli

vue-cli就是一个脚手架,步骤很简单,输入几个命令之后就会生成整个项目,里面包括了webpack、ESLint、babel很多配置等等

vue-cli创建项目

1
2
3
4
5
vue --help
# 下面命令中就有如何创建一个项目
vue-init --help
# 执行创建命令后等待就可以了
vue init webpack vue_1

项目参数

2019-04-23 10-01-02 的屏幕截图.png

这里有几个需要说明一下,没说明的直接回车选择默认就好,对项目没多大影响,很傻瓜式:

  • Project name: 这个是项目名称,默认是输入时的那个名称,想改的话直接输入修改,也可以直接回车

  • Install vue-route: 是否需要vue-router,这里默认选择使用,这样生成好的项目就会有相关的路由配置文件

  • Use ESLint to lint your code: 是否使用ESLint,刚才说了我们这个项目需要使用所以也是直接回车,默认使用,这样会生成相关的ESLint配置

  • Setup unit tests with Karma + Moch?: 是否安装单元测试。由于我们现在还没有单元测试,所以这里选择的是”N”,而不是直接回车哦

  • Setup e2e tests with Nightwatch: 是否安装e2e测试,这里我也同样选择的是“N”

这几个配置选择yes 或者 no 对于我们项目最大的影响就是,如果选择了yes 则生成的项目会自动有相关的配置,有一些loader我们就要配套下载。所以如果我们确定不用的话最好不要yes,要么下一步要下很多没有用的loader

项目目录:

2019-04-23 10-05-46 的屏幕截图.png

1. build 文件夹:

1
2
3
4
5
6
7
8
build.js           
logo.png
vue-loader.conf.js
webpack.dev.conf.js
check-versions.js
utils.js
webpack.base.conf.js
webpack.prod.conf.js

如上,这个文件夹主要是进行webpack的一些配置,就我个人觉得啊~对我们最有用并且可能会使用的就是webpack.base.config.js、webpack.dev.config.js、webpack.prod.config.js三个webpack的配置文件,分别是基本webpack配置、开发环境配置、生产环境配置。实际上这些文件里面的内容,一些简单的配置都已经有了,包括入口文件、插件、loader、热更新等都已经配置好了。我们要做的只是根据自己的项目有什么loader需要增加的,比如生成环境需要加上UglifyJsPlugin插件等可以自行配置,或者一些插件增加或者不需要的删除,其实都是和业务相关了,其他的都可以不需要动

2. config 文件夹:

1
2
3
4
4月 23 09:59 dev.env.js
4月 23 09:59 index.js
4月 23 09:59 prod.env.js
4月 23 09:59 test.env.js

这几个配置文件我觉得最主要的就是index.js 这个文件进行配置代理服务器,这个地方和我们息息相关,和后台联调就是在这里设置一个地址就可以了。打开index.js 找到“proxyTable“这个属性,然后在里面加上对应的后台地址即可 ,构建生成环境也在这里配置,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.

const path = require('path')

module.exports = {
dev: {

// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},

// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-

// Use Eslint Loader?
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: true,
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false,

/**
* Source Maps
*/

// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-module-eval-source-map',

// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,

cssSourceMap: true
},

build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),

// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',

/**
* Source Maps
*/

productionSourceMap: true,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',

// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],

// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}

3. src文件夹:

1
2
3
4
5
4月 23 09:59 assets
4月 23 09:59 components
4月 23 09:59 router
4月 23 09:59 App.vue
4月 23 09:59 main.js

这个文件夹是整个项目最主要以及使用频率最高的文件夹。

  • “assets”: 共用的样式、图片

  • “components”: 业务代码存放的地方,里面分成一个个组件存放,一个页面是一个组件,一个页面里面还会包着很多组件

  • “router”: 设置路由

  • “App.vue”: vue文件入口界面

  • “main.js:对应App.vue 创建vue实例,也是入口文件,对应webpack.base.config.js里的入口配置

4.static 文件夹:

存放的文件不会经过webpack处理,可以直接引用,例如swf文件如果要引用可以在webpack配置对swf后缀名的文件处理的loader,也可以直接将swf文件放在这个文件夹引用

5.package.json:

这个文件有两部分是有用的:scripts 里面设置命令,例如设置了dev用于调试则我们开发时输入的是
npm run dev ;例如设置了build 则是输入 npm run build 用于打包;另一部分是这里可以看到我们需要的依赖包,在dependencies和devDependencies中,分别对应全局下载和局部下载的依赖包

下载依赖包

1
npm install --registry=https://registry.npmmirror.com

运行 npm run dev

当所有依赖包都下载好之后,输入命令:npm run dev 运行就可以看到一个自带的默认页面打开。此时项目就已经全部搭建好并且运行了~炒鸡简单吧,总结下来其实只有四步

  • npm install --global vue-cli 下载vue-cli脚手架
  • vue init webpack vue_1 生成项目,形成基本结构
  • npm install # 依赖包
  • npm run dev 运行

打包 npm run build

第一个demo

Webpack 入门教程

分类 编程技术

Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。

来自runoob

从图中我们可以看出,Webpack 可以将多种静态资源 js、css、less 转换成一个静态文件,减少了页面的请求。

接下来我们简单为大家介绍 Webpack 的安装与使用。

创建项目

接下来我们创建一个目录 app:

1
mkdir app

在 app 目录下添加 maxzhao1.js 文件,代码如下:

app/maxzhao1.js 文件

1
document.write("It works.");

在 app 目录下添加 index.html 文件,代码如下:

app/index.html 文件

1
2
3
4
5
6
7
8
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script type="text/javascript" src="maxzhao_bundle.js" charset="utf-8"></script>
</body>
</html>

接下来我们使用 webpack 命令来打包:

1
webpack-cli  maxzhao1.js -o  maxzhao_bundle.js

执行以上命令会编译 maxzhao1.js 文件并生成maxzhao_bundle.js 文件,成功后输出信息如下所示:

1
2
3
4
5
6
Hash: a41c6217554e666594cb
Version: webpack 1.12.13
Time: 50ms
Asset Size Chunks Chunk Names
bundle.js 1.42 kB 0 [emitted] main
[0] ./maxzhao1.js 29 bytes {0} [built]

在浏览器中打开 index.html,输出结果如下:

img

创建第二个 JS 文件

接下来我们创建另外一个 js 文件 maxzhao2.js,代码如下所示:

app/maxzhao2.js 文件

1
module.exports = "It works from maxzhao2.js.";

更新 maxzhao1.js 文件,代码如下:

1
document.write(require("./maxzhao2.js"));

接下来我们使用 webpack 命令来打包:

1
webpack maxzhao1.js -o maxzhao_bundle.js
1
2
3
Entrypoint main = maxzhao_bundle.js
[0] ./maxzhao1.js 42 bytes {0} [built]
[1] ./maxzhao2.js 46 bytes {0} [built]

在浏览器访问index.html,输出结果如下所示:

2019-04-24 11-24-35 的屏幕截图.png

接下来大家可以移步RUNOOB

Loader:加载其它文件

Webpack 本身只能处理 JavaScript 模块,如果要处理其他类型的文件,就需要使用 loader 进行转换。

所以如果我们需要在应用中添加 css 文件,就需要使用到 css-loader 和 style-loader,他们做两件不同的事情,css-loader 会遍历 CSS 文件,然后找到 url() 表达式然后处理他们,style-loader 会把原来的 CSS 代码插入页面中的一个 style 标签中。

安装css-loaderstyle-loader

接下来我们使用以下命令来安装css-loaderstyle-loader(全局安装需要参数 -g)。

1
2
3
4
npm i css-loader style-loader
ls
# 多了一个文件夹 node_module 和文件 pack-lock.json
# ` node_modules `目录,它就是 css-loader 和 style-loader 的安装目录。

使用

接下来创建一个 style.css 文件,代码如下:

1
body {     background: yellow; }

修改 maxzhao1.js 文件,代码如下:

1
2
require("!style-loader!css-loader!./style.css");
document.write(require("./maxzhao2.js"));

编译:webpack maxzhao1.js -o maxzhao_bundle.js

2019-04-24 11-40-03 的屏幕截图.png

require CSS文件的时候都要写 loader前缀 !style-loader!css-loader!,当然我们可以根据模块类型(扩展名)来自动绑定需要的loader。 将maxzhao1.js 中的 require("!style-loader!css-loader!./style.css") 修改为require("./style.css")

1
require("./style.css"); document.write(require("./maxzhao2.js"));

然后执行:

1
webpack maxzhao1.js maxzhao_bundle.js --module-bind 'css=style-loader!css-loader'

配置文件:配置编辑选项

我们可以将一些编译选项放在配置文件中,以便于统一管理:

创建 webpack.config.js 文件,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
module.exports = {
entry: "./maxzhao1.js",
output: {
path: __dirname,
filename: "maxzhao_bundle.js"
},
module: {
loaders: [
{ test: /\.css$/, loader: "style-loader!css-loader" }
]
}
};

接下来我们只需要执行webpack命令即可生成maxzhao_bundle.js文件:

1
webpack   Hash: 4fdefac099a5f36ff74b Version: webpack 1.12.13 Time: 576ms     Asset     Size  Chunks             Chunk Names bundle.js  11.8 kB       0  [emitted]  main    [0] ./runoob1.js 65 bytes {0} [built]    [5] ./runoob2.js 46 bytes {0} [built]     + 4 hidden modules

webpack 命令执行后,会默认载入当前目录的webpack.config.js文件。

其它

vue-router懒加载

单页面应用,如果没有应用懒加载,第一次进入时,需要加载的内容过多,会出现长时间的白屏,不利于用户体验,运用懒加载则可以将页面进行划分,需要的时候加载相对应的页面,可以有效减少第一次加载的压力和用时。

router文件夹下的index.js原路由加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'

Vue.use(Router)

export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})

修改为懒加载

1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export default new Router({
routes: [
{
path: '/',
component: resolve => require(['@/components/HelloWorld'], resolve)
}
]
})

配置sass及屏幕适配

配置sass

1.安装sass-loader及node-sass

1
npm install sass-loader node-sass --save-dev

2.使用
在.vue文件中的style中添加 lang=’scss’,例如

1
2
3
4
5
6
<style scoped lang='scss'>
$t-base:#32ba90;
.wrap{
color: $t-base;
}
</style>

移动端—屏幕适配

1.在src文件夹中新建一个文件夹styles,并新建两个scss文件 — mixin.scss / reset.scss
mixin.scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$SCALE: 10;
$BASE: 375 / $SCALE;//375为设计图尺寸

// 超过显示省略号
@mixin ellipsis {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
//多行文本,超过显示省略号
@mixin moreLine($num){
display: -webkit-box; /** 对象作为伸缩盒子模型显示 **/
-webkit-box-orient: vertical; /** 设置或检索伸缩盒对象的子元素的排列方式 **/
-webkit-line-clamp: $num; /** 显示的行数 **/
overflow: hidden; /** 隐藏超出的内容 **/
}

@function rem ($px) {
@return ($px / $BASE) + rem
}

reset.scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@import './mixin.scss';

$size_small:rem(12);
$size_middle:rem(14);
$size_default:rem(16);
$size_big:rem(18);
$text_color: #666;
$bg_color: #32ba90;
$border_color: #dedede;

body, div, span, header, footer, nav, section, aside, article, ul, dl, dt, dd, li, a, p, h1, h2, h3, h4,h5, h6, i, b, em,textarea, button, input, select, figure, figcaption {
padding: 0;
margin: 0;
list-style: none;
text-decoration: none;
border: none;
font-weight: normal;
font-family: PingFangSC-Light,helvetica,'Heiti SC';
box-sizing: border-box;
font-size: 14px;
-webkit-tap-highlight-color:transparent;
-webkit-font-smoothing: antialiased;
&:hover{
outline: none;
}
}
a, a:visited {
text-decoration: none;
color: #333;
}
input:focus {
outline: none;
border: none;
}
html, body {
width: 100%;
height: 100%;
-webkit-font-smoothing: antialiased;
background: #fff;
color: #333;
padding: 0;
margin: 0;
}
// 禁止用户选中
body{
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
::-webkit-scrollbar {
display: none;
}

.fl{
float: left;
}
.fr{
float: right;
}
.clearfix:after{ /*最简方式*/
content: '';
display: block;
clear: both;
}
.clearfix{ /*兼容 IE*/
zoom: 1;
}

2.在App.vue文件中设置html的font-size

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
import {mapGetters,mapActions} from 'vuex'
export default {
name: 'App',
mounted () {
// 设置html的font-size
document.addEventListener('DOMContentLoaded', function () {
const html = document.querySelector('html');
html.style.fontSize = window.innerWidth / 10 + 'px';
});
}
}
</script>

3.mixin.scss则需要在所需的.vue文件中的style中引入,例如components中的HelloWorld.vue

1
2
3
4
<style scoped lang='scss'>
@import '../styles/reset.scss';
/*可使用mixin和reset中的变量及方法*/
</style>

数据请求axios

1.安装axios

1
npm install axios --save

2.在src文件夹中新建api文件夹,并新建index.js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import axios from 'axios';
var qs = require('qs');


if (process.env.NODE_ENV === 'development') {
axios.defaults.baseURL = ''; //''里面填写请求域名
}

import VueRouter from 'vue-router';


//get请求方式
export const home = () => {
return axios.get('/api/index');
}
//post请求方式(包含参数)
export const article = (id)=>{
const params = qs.stringify({'id':id});
return axios.post('/api/article', params);
}

3.调用接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
import {home} from '@/api';//引入

export default {
data () {
return {
}
},
mounted () {
const self = this;
home().then(r => {//调用接口
let res = r.data;
console.log(res)
});
},
}
</script>

Toast提示

img

1.创建一个普通的Toast提示组件

src/components/toast/index.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<template>
<div v-if="showToast" class="toast" >
<p>{{ toastTitle }}</p>
</div>
</template>

<script>
export default {
data: () => ({
showToast: false,
toastTitle: '',
}),

methods: {
show (params) {
this.toastTitle = params;
this.showToast = true
},

},
watch: {
toastTitle: function(val) {
if (val) {
this.showToast = true;
setTimeout(() => {
this.showToast = false;
this.toastTitle='';
}, 2000)
}
return val;
}
}
}
</script>

<style scoped lang='scss'>
@import '../../styles/mixin.scss';

.toast {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(49, 49, 49, 0.8);
color: #fff;
border-radius: rem(5);
z-index: 999;
text-align: center;
font-size: rem(14);
p{
margin: 0;
padding: rem(4) rem(10);

}
}
</style>

2.将组件注册成为plugin

src/components/toast/plugin.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Toast from './index'

export default {
install (Vue, options = {}) {
const VueToast = Vue.extend(Toast)
let toast = null

function $toast (params) {
return new Promise(resolve => {
if (!toast) {
toast = new VueToast()
toast.$mount()
document.querySelector(options.container || 'body').appendChild(toast.$el)
}
toast.show(params)
resolve()
})
}

Vue.prototype.$toast = $toast
}
}

3.在main.js中载入

1
2
3
//toast
import toast from './components/toast/plugin'
Vue.use(toast)

4.在页面中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div class="wrap">
<button class="btn" @click="showToast">show toast</button>
</div>
</template>

<script>
export default {
data () {
return {

}
},
methods: {
showToast () {
this.$toast('toast')
}
}
}
</script>

第一个项目

前言

我这里安装好之后直接就带router了,对与我这个没入门的人来说有点难度,但我就喜欢迎难而上!

下面所有的介绍都会包含路由了,刚刚开始的时候,会避开路由。

插曲:我的系统是Manjaro 所以我安装了atom,挺方便快捷的;

创建

1
2
3
4
vue-init webpack vue_maxzhao
# 一路回车就可以了
cd vue_maxzhao
npm i

查看基础帮助

1
cat README.md

启动

1
npm run dev

打开http://localhost:8080文件就会出现默认效果。

测试基础模块语法

Vue.js 使用了基于 HTML 的模版语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。

Vue.js 的核心是一个允许你采用简洁的模板语法来声明式的将数据渲染进 DOM 的系统。

结合响应系统,在应用状态改变时, Vue 能够智能地计算出重新渲染组件的最小代价并应用到 DOM 操作上。

插值

文本

数据绑定最常见的形式就是使用

1
{{...}}

(双大括号)的文本插值:

NPM 帮助

安装模块

1
2
3
4
5
6
7
8
9
10
11
12
dependencies
装载:npm install vue --save-prod
卸载:npm uninstall vue --prod
devDependencies
装载:npm install vue --save-dev
卸载:npm uninstall vue --dev

-P 相当于 --save-prod, 添加dependencies 里面所有的包。在 -D -O 不存在时,-P 就是默认值 。 也可以写全称: --save-production
-S 相当于 --save; 添加dependencies 里面所有的包。
-D 相当于 --save-dev; 添加devDependencies 里面所有的包,
-O 相当于 --save-optional, 添加在 optionalDependencies 里面的包,
--no-save: 阻止保存记录在dependencies 中。

更新模块

1
2
3
4
5
6
# 安装插件
sudo npm i -g npm-check-updates
# 更新 package.json 中的版本
ncu -u
# 安装更新
npm i

错误问题

To install it, you can run: npm install –save !!vue-style-loader!css-loader?

1
npm install node-sass -D

然后参考 上面的模块更新操作.

Module build failed: TypeError: this.getResolve is not a function

sass-loader版本太高了

1
"sass-loader": "^7.3.1",

本文地址:Vue我的首次遇见

本文地址: https://github.com/maxzhao-it/blog/post/20956/

自带注解

@ModelAttribute

作用在接口方法或接口参数上

在接口方法上时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//没有返回值 为void  应该是无论请求什么接口 会率先执行
@ModelAttribute
public void populateModel(ModelMap model) {
model.addAttribute("attributeName", "123");
}
//不指定指定属性名称,方法返回一个对象,相当于model.addAttribute("user", user)
@ModelAttribute
public User addUser() {
User user = new User();
return user;
}
//或者 model.addAttribute("aaaa", user) 相同
@ModelAttribute("aaaa")
public User addUser() {
User user = new User();
return user;
}
//指定属性名称,方法返回一个字符串,相当于model.addAttribute("string1-key", "string1-value")
@ModelAttribute("string1-key")
public String addString() {
return "string1-value";
}

参数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//从模型中获取一个属性值,将其转换到对应类型的变量中 可以理解成解析对应的jsp中   abc.name   abc.password  然后提交过来 用user解析

@RequestMapping(value = "helloWorld")
public String helloWorld(@ModelAttribute("abc") User user,
@ModelAttribute("attributeName") String aName,
@ModelAttribute("string1-value") String svalue) {
return "helloWorld";
}

//从Form表单或者URL参数中获取属性参数值,放到对应类型的参数中,注意:此时表单中的组件名和参数属性名称一致,如User对象有两个属性,分别为username,password,则表单中input的名称必须为username,password,才能实现属性值注入。
//注意这个User类必须要有无参数的构造函数或者是setter方法
//此时@ModelAttribute可以不用显式写
@RequestMapping(value = "helloWorld2")
public String helloWorld2(@ModelAttribute("user") User user) {
System.out.println("---helloWorld---"+user.getUsername()+", "+user.getPassword());
return "helloWorld";
}

对比RequestParam、RequestBody

1
2
3
1. application/x-www-form-urlencoded,这种情况的数据@RequestParam、@ModelAttribute可以处理,@RequestBody也可以处理。
2. multipart/form-data,@RequestBody不能处理这种格式的数据。(form表单里面有文件上传时,必须要指定enctype属性值为multipart/form-data,意思是以二进制流的形式传输文件。)
3. application/json、application/xml等格式的数据,必须使用@RequestBody来处理。

@Order(n):bean的加载顺序

1
优先级 1 2 3 ...

@EnableAsync:开启异步(多线程)

@Async:异步

可以注解在类或方法上,但不能是 static 修改

异步使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 @Async
public void dealNoReturnTask() {
logger.info("返回值为void的异步调用开始" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("返回值为void的异步调用结束" + Thread.currentThread().getName());
}
// Future<String> 回调函数
@Async
public Future<String> dealHaveReturnTask(int i) {
logger.info("asyncInvokeReturnFuture, parementer=" + i);
Future<String> future;
try {
Thread.sleep(1000 * i);
future = new AsyncResult<String>("success:" + i);
} catch (InterruptedException e) {
future = new AsyncResult<String>("error");
}
return future;
}

判断是否执行结束

1
2
Future<String> f = Future<String>(1);
f.isDone()

@Converter(autoApply = true)

Spring data jpa 中的实体属性转换器

@EnableScheduling:启动定时任务

@Scheduled:定时任务注解

在方法上添加@Scheduled就可以定时执行了。

  • @Scheduled(fixedRate = 5000) :上一次开始执行时间点之后5秒再执行
  • @Scheduled(fixedDelay = 5000) :上一次执行完毕时间点之后5秒再执行
  • @Scheduled(initialDelay=1000, fixedRate=5000) :第一次延迟1秒后执行,之后按fixedRate的规则每5秒执行一次
  • @Scheduled(cron="*/5 * * * * *") :通过cron表达式定义规则

推荐

在线cron表达式网址

@EnableConfigurationProperties:读入自动配置类

比如

1
@ConfigurationProperties(prefix = "spring.datasource")

@ConditionalOnClass:加载自动配置类

1
2
3
4
5
6
@Configuration //配置类
//这里就是前面说的,这个注解读入我们的配置对象类
@EnableConfigurationProperties(HelloProperties.class)
//当类路径存在这个类时才会加载这个配置类,否则跳过,这个很有用比如不同jar包间类依赖,依赖的类不存在直接跳过,不会报错
@ConditionalOnClass(HelloService.class)
// HelloService 类上不需要加 Component

@EnableAutoConfiguration

不要自己添加

@Primary 优先选择

@Configuration 把一个类作为一个IoC容器

它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。

@Scope注解 作用域

@Lazy(true) 表示延迟初始化

@Service用于标注业务层组件、

@Controller用于标注控制层组件

如struts中的action)

@Repository 标注数据访问组件

即DAO组件。

@Component 组件<泛指>,当组件不好归类的时候

@PostConstruct 用于指定初始化方法(用在方法上)

@PreDestory 用于指定销毁方法(用在方法上)

@Resource 默认按名称装配,当找不到与名称匹配的bean才会按类型装配。

@DependsOn:定义Bean初始化及销毁时的顺序

@Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常

@Autowired 默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用

@Autowired @Qualifier(“personDaoBean”) 存在多个实例配合使用

@ConditionalOnMissingBean

当前bean 不存在时,使用备用bean

1
2
3
4
5
6
7
8
9
10
11
@Configuration
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {

@Bean
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}

}

@Value

1
2
@Value(value = "${snowflake.data_center_id}")
private long dataCenterId = -1L;

@ConfigurationProperties(prefix = “”)

自定义配置类, prefix 为前缀.

比如 DataSourceProperties

1
2
3
4
5
6
7
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
private String driverClassName;
private String url;
private String username;
private String password;
}

对于下面的配置,上面会自动注入

1
2
3
4
5
6
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/boot?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: root

@NestedConfigurationProperty

使用于 @ConfigurationProperties(prefix = "")中, 主要是为了嵌套配置, 比如当前配置中有 MapListclass等类型的属性时,需要用 @NestedConfigurationProperty 标记。

用来指示应该将常规(非内部)类视为嵌套类。

比如 MybatisPlusProperties

1
2
3
4
5
6
7
@ConfigurationProperties(prefix = Constants.MYBATIS_PLUS)
public class MybatisPlusProperties {
@NestedConfigurationProperty
private MybatisConfiguration configuration;
@NestedConfigurationProperty
private GlobalConfig globalConfig = GlobalConfigUtils.defaults();
}

自定义注解

1、元注解(meta-annotation):

元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:
    1.@Target,
    2.@Retention,
    3.@Documented,
    4.@Inherited
  这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和相应分参数的使用说明。

@Target:

   @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

  作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

  取值(ElementType)有:

    1.CONSTRUCTOR:用于描述构造器
    2.FIELD:用于描述域
    3.LOCAL_VARIABLE:用于描述局部变量
    4.METHOD:用于描述方法
    5.PACKAGE:用于描述包
    6.PARAMETER:用于描述参数
    7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

1
2
3
4
5
6
7
8
9
10
11
12
13
@Target(ElementType.TYPE)
public @interface Table {
/**
* 数据表名称注解,默认值为类名称
* @return
*/
public String tableName() default "className";
}

@Target(ElementType.FIELD)
public @interface NoDBColumn {

}

注解Table 可以用于注解类、接口(包括注解类型) 或enum声明,而注解NoDBColumn仅可用于注解类的成员变量。

@Retention:

  @Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

  作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

  取值(RetentionPoicy)有:

    1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在运行时有效(即运行时保留)

  Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。具体实例如下:

1
2
3
4
5
6
7
8
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}

Column注解的的RetentionPolicy的属性值是RUTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理


  @Documented:

  **@**Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

1
2
3
4
5
6
7
8
9
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}

@Inherited:

  @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

  注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

  当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

  实例代码:

1
2
3
4
5
6
7
8
9
10
11
/**
*
* @author peida
*
*/
@Inherited
public @interface Greeting {
public enum FontColor{ BULE,RED,GREEN};
String name();
FontColor fontColor() default FontColor.GREEN;
}

自定义注解:

 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

  定义注解格式:
  public @interface 注解名 {定义体}

  注解参数的可支持数据类型:

    1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
    2.String类型
    3.Class类型
    4.enum类型
    5.Annotation类型
    6.以上所有类型的数组

  Annotation类型里面的参数该怎么设定:
  第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;   
  第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;  
  第三,如果只有一个参数成员,最好把参数名称设为”value”,后加小括号.例:下面的例子FruitName注解就只有一个参数成员。

  简单的自定义注解和使用注解实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 水果名称注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 水果颜色注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
/**
* 颜色枚举
* @author peida
*
*/
public enum Color{ BULE,RED,GREEN};

/**
* 颜色属性
* @return
*/
Color fruitColor() default Color.GREEN;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package annotation;

import annotation.FruitColor.Color;

public class Apple {

@FruitName("Apple")
private String appleName;

@FruitColor(fruitColor=Color.RED)
private String appleColor;




public void setAppleColor(String appleColor) {
this.appleColor = appleColor;
}
public String getAppleColor() {
return appleColor;
}


public void setAppleName(String appleName) {
this.appleName = appleName;
}
public String getAppleName() {
return appleName;
}

public void displayName(){
System.out.println("水果的名字是:苹果");
}
}
1
2
3
4
5
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAccess {
String desc() default "无信息";
}

用反射机制来调用注解中的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.dragon.test.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
* 用反射机制来调用注解中的内容
* Created by gmq on 2015/9/10.
*/
public class MyReflection
{
public static void main(String[] args) throws Exception
{
// 获得要调用的类
Class<MyTest> myTestClass = MyTest.class;
// 获得要调用的方法,output是要调用的方法名字,new Class[]{}为所需要的参数。空则不是这种
Method method = myTestClass.getMethod("output", new Class[]{});
// 是否有类型为MyAnnotation的注解
if (method.isAnnotationPresent(MyAnnotation.class))
{
// 获得注解
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
// 调用注解的内容
System.out.println(annotation.hello());
System.out.println(annotation.world());
}
System.out.println("----------------------------------");
// 获得所有注解。必须是runtime类型的
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations)
{
// 遍历所有注解的名字
System.out.println(annotation.annotationType().getName());
}
}
}

自定义注解还可以放入AOP

注解 @UserAccess

1
2
3
4
5
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAccess {
String desc() default "无信息";
}

AOP 中的使用

1
2
3
4
@After("@annotation(userAccess)")
public void after(JoinPoint jp, UserAccess userAccess) {
log.debug("second after:" + userAccess.desc());
}

本文地址: https://github.com/maxzhao-it/blog/post/44534/

前言

第二次要与VUE见面了,好兴奋啊!虽然不知道为啥兴奋呢!

Vue我的首次遇见之后,我又要开始我的“操作了”。

基本创建都在 Vue我的首次遇见 中。

这里面内容大都来自runoob
因为我这里用的是router,所以作了部分修改

Vue.js 模板语法

Vue.js 使用了基于 HTML 的模版语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。

Vue.js 的核心是一个允许你采用简洁的模板语法来声明式的将数据渲染进 DOM 的系统。

结合响应系统,在应用状态改变时, Vue 能够智能地计算出重新渲染组件的最小代价并应用到 DOM 操作上。


插值

文本

数据绑定最常见的形式就是使用

1
{{...}}

(双大括号)的文本插值:

打开 index.html

1
2
3
4
5
6
7
<div id="app"></div>
<!-- built files will be auto injected -->
<div id="vue_det">
<h1>author : {{author}}</h1>
<h1>date : {{date}}</h1>
<h1>{{description()}}</h1>
</div>

打开 src/main.js,这里面放的是当前routerjs脚本的地方。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 在最后添加 ,因为最上面已经添加了 import Vue from 'vue' 所以就可以直接用了
// eslint-disable-next-line no-unused-vars
new Vue({
el: '#vue_det',
data: {
author: 'maxzhao',
date: '2019-04-24'
},
methods: {
description: function () {
return this.author + ' - 学的不仅是技术,更是梦想!'
}
}
})

保存之后刷新页面(如果是按照我的安装,则自动添加热加载);

image.png

Html

使用 v-html 指令用于输出 html 代码:

打开 index.html

1
2
3
4
5
6
7
8
<div id="app"></div>
<!-- built files will be auto injected -->
<div id="vue_det">
<h1>author : {{author}}</h1>
<h1>date : {{date}}</h1>
<h1>{{description()}}</h1>
<div v-html="message"></div>
</div>

打开 src/main.js

1
2
3
4
5
6
7
8
new Vue({
el: '#vue_det',
data: {
author: 'maxzhao',
date: '2019-04-24',
message: '<h1>maxzhao_v-html</h1>'
},
//****

自己测试前段界面哦。

属性

HTML 属性中的值应使用 v-bind 指令。

以下实例判断 class1 的值,如果为 true 使用 class1 类的样式,否则不使用该类:

v-bind 指令

打开 index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>vue_maxzhao</title>
<style>
.class1{
background: #444;
color: #eee;
}
</style>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<div id="vue_det">
<h1>author : {{author}}</h1>
<h1>date : {{date}}</h1>
<h1>{{description()}}</h1>
<div v-html="message"></div>
<label for="r1">修改颜色</label><input type="checkbox" v-model="use" id="r1">
<br><br>
<div v-bind:class="{'class1': use}">
v-bind:class 指令
</div>
</div>
</body>
</html>

打开 src/main.jsdata 中添加,use: false

表达式

Vue.js 都提供了完全的 JavaScript 表达式支持。

打开 index.html

1
2
3
4
5
6
7
8
9
<!--  **   -->      
<div v-bind:class="{'class1': use}">
v-bind:class 指令
</div>
<br>
{{5+5}}<br>
{{ ok ? 'YES' : 'NO' }}<br>
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id">maxzhao</div>

打开 src/main.jsdata 中添加

1
2
3
use: false,
ok: true,
id: 1

指令

指令是带有 v- 前缀的特殊属性。

指令用于在表达式的值改变时,将某些行为应用到 DOM 上。如下例子:

v-if

打开 index.html,接着添加:

1
2
3
4
5
6
7
<br>
<p v-if="seen">现在你看到我了</p>
<template v-if="ok">
<h1>maxzhao</h1>
<p>学的不仅是技术,更是梦想!</p>
<p>哈哈哈,打字辛苦啊!!!</p>
</template>

打开 src/main.jsdata 中添加

1
seen: true

这里, v-if 指令将根据表达式 seen 的值(true 或 false )来决定是否插入 p 元素。

参数

参数在指令后以冒号指明。例如, v-bind 指令被用来响应地更新 HTML 属性:

打开 index.html,接着添加:

1
2
<br>
<pre><a v-bind:href="url">菜鸟教程</a></pre>

打开 src/main.jsdata 中添加

1
url: 'http://localhost:8080'

在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url 的值绑定。

另一个例子是 v-on 指令,它用于监听 DOM 事件:

打开 index.html,接着添加:

1
<pre><a v-on:click="alertMsg">弹出警示</a></pre>

打开 src/main.jsmethods 中添加

1
2
3
4
5
6
7
8
methods: {
description: function () {
return this.author + ' - 学的不仅是技术,更是梦想!'
},
alertMsg: function () {
alert('弹出警示')
}
}

在这里参数是监听的事件名。

修饰符

修饰符是以半角句号.指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()

1
<form v-on:submit.prevent="onSubmit"></form>


用户输入

在 input 输入框中我们可以使用 v-model 指令来实现双向数据绑定:

双向数据绑定

打开 index.html,接着添加:

1
2
<p>{{ message }}</p>
<input v-model="message">

v-model 指令用来在 input、select、textarea、checkbox、radio 等表单控件元素上创建双向数据绑定,根据表单上的值,自动更新绑定的元素的值。

按钮的事件我们可以使用 v-on 监听事件,并对用户的输入进行响应。

以下实例在用户点击按钮后对字符串进行反转操作:

字符串反转

打开 index.html,接着添加:

1
2
<p>{{ message }}</p>
<input v-model="message">

main.js中的 methods 中添加

1
2
3
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}

现在大家已经可以知道数据和方法的“存放处”了,接下来就不在详细叙述了,只是走流程。


过滤器

Vue.js 允许你自定义过滤器,被用作一些常见的文本格式化。由”管道符”指示, 格式如下:

1
<h1>{{ message | reverseMessage }}</h1>

过滤器函数接受表达式的值作为第一个参数。

以下实例对输入的字符串第一个字母反转

1
2
3
4
5
6
7
8
9
10
11
12
13
// 与methods同级别添加filters属性
filters: {
reverseMessage: function (value) {
if (!value) return ''
value = value.toString()
return value.split('').reverse().join('')
},
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(11).toUpperCase() + value.slice(1)
}
}

过滤器可以串联:

1
<h1>{{ message |reverseMessage | capitalize }}</h1>

过滤器是 JavaScript 函数,因此可以接受参数:

1
<h1>{{ message |reverseMessage | capitalize | addStr('--maxzhao--') }}</h1>
1
2
3
4
5
addStr: function (value, addStr) {
if (!value) return ''
value = value.toString()
return value + addStr
}

这里,message 是第一个参数,字符串 ‘arg1’ 将传给过滤器作为第二个参数, arg2 表达式的值将被求值然后传给过滤器作为第三个参数。


缩写

v-bind 缩写

Vue.js 为两个最为常用的指令提供了特别的缩写:

1
2
<!-- 完整语法 -->  <a  v-bind:href="url"></a>  
<!-- 缩写 --> <a :href="url"></a></pre>

v-on 缩写

1
2
3
<a  v-on:click="doSomething"></a>  
<!-- 缩写 -->
<a @click="doSomething"></a>

起步结束了,下面开始一些有难度的操作(学过之后简直毫无难度)。

Vue.js 条件与循环

条件判断

v-if 指令

条件判断使用 v-if 指令:

在元素 和 template 中使用 v-if 指令:

1
2
3
4
5
<template v-if="ok">
<h1>maxzhao</h1>
<p>学的不仅是技术,更是梦想!</p>
<p>哈哈哈,打字辛苦啊!!!</p>
</template>

这里, v-if 指令将根据表达式 ok的值(true 或 false )来决定是否插入template元素。

在字符串模板中,如 Handlebars ,我们得像这样写一个条件块:

1
2
3
4
<!-- Handlebars 模板,暂时还不知道有啥用 -->
{{#if ok}}
<h1>Yes</h1>
{{/if}}

v-else 指令

可以用 v-else 指令给 v-if 添加一个 “else” 块:

随机生成一个数字,判断是否大于0.5,然后输出对应信息:

1
2
3
4
5
6
<div v-if="Math.random() > 0.5">
Sorry
</div>
<div v-else>
Not sorry
</div>

v-else-if 指令

v-else-if 在 2.1.0 新增,顾名思义,用作 v-if 的 else-if 块。可以链式的多次使用:

判断 type 变量的值:

1
2
3
4
5
6
7
8
9
10
11
12
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
1
2
3
data: {
type: 'C'
}

v-else 、v-else-if 必须跟在 v-if 或者 v-else-if之后。

v-show 指令

我们也可以使用 v-show 指令来根据条件展示元素:

1
2
<h1 v-show="true">Hello!</h1>
<h1 v-show="false">don't show!</h1>

Vue.js 循环语句

v-for 指令

循环使用 v-for 指令。

v-for 指令需要以 site in sites 形式的特殊语法, sites 是源数据数组并且 site 是数组元素迭代的别名。

v-for 可以绑定数据到数组来渲染一个列表:

1
2
3
4
5
<ol>
<li v-for="site in sites">
{{ site.name }}
</li>
</ol>
1
2
3
4
5
6
7
data: {
sites: [
{ name: 'Runoob' },
{ name: 'Google' },
{ name: 'Taobao' }
]
}

模板中使用 v-for:

v-for 迭代对象

v-for 可以通过一个对象的属性来迭代数据:

1
2
3
4
5
<ul>
<li v-for="value in object">
{{ value }}
</li>
</ul>
1
2
3
4
5
6
7
8
  data: {
object: {
name: '菜鸟教程',
url: 'http://www.runoob.com',
slogan: '学的不仅是技术,更是梦想!'
}
}
})

第二个的参数为键名:

1
2
3
4
5
6
  <ul>
<li v-for="(value, key) in object">
{{ key }} : {{ value }}
</li>
</ul>
</div>

第三个参数为索引:

1
2
3
4
5
<ul>
<li v-for="(value, key, index) in object">
{{ index }}. {{ key }} : {{ value }}
</li>
</ul>

v-for 迭代整数

v-for 也可以循环整数

1
2
3
4
5
<ul>
<li v-for="n in 10">
{{ n }}
</li>
</ul>

v-for指令界面效果

2019-04-25 11-49-53 的屏幕截图.png

Vue.js 计算属性

计算属性关键词: computed。

计算属性在处理一些复杂逻辑时是很有用的。

可以看下以下反转字符串的例子:

1
2
3
4
5
6
<div>
{{ message.split('').reverse().join('') }}
</div>
<div>
{{ reversedMessage }}
</div>
1
2
3
4
5
6
7
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}

实例中声明了一个计算属性 reversedMessage 。

提供的函数将用作属性 vm.reversedMessage 的 getter 。

vm.reversedMessage 依赖于 vm.message,在 vm.message 发生改变时,vm.reversedMessage 也会更新。

computed vs methods

我们可以使用 methods 来替代 computed,效果上两个都是一样的,但是 computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值。而使用 methods ,在重新渲染的时候,函数总会重新调用执行。

可以说使用 computed 性能会更好,但是如果你不希望缓存,你可以使用 methods 属性。

computed setter

computed 属性默认只有 getter ,不过在需要时你也可以提供一个 setter :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var vm = new Vue({
el: '#vue_det',
data: {
//****
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
},
site: {
// getter
get: function () {
return this.author + ' ' + this.slogan
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.author = names[0]
this.slogan = names[names.length - 1]
}
}
}
})
// 调用 setter, vm.name 和 vm.url 也会被对应更新
vm.site = 'maxzhao 学的不仅是技术,更是梦想!'
document.write('author: ' + vm.author)
document.write('<br>')
document.write('slogan: ' + vm.slogan)
document.write('<br>')
document.write('site: ' + vm.site)

引:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 把代码改了改,应该可以体现 computer 属性“依赖缓存”的概念以及与 method 的差别。如下面代码,cnt 是独立于 vm 对象的变量。在使用 reversedMessage 这个计算属性的时候,第一次会执行代码,得到一个值,以后再使用 reversedMessage 这个计算属性,因为 vm 对象没有发生改变,于是界面渲染就直接用这个值,不再重复执行代码。而 reversedMessage2 没有这个缓存,只要用一次,函数代码就执行一次,于是每次返回值都不一样。

var cnt=1;
var vm = new Vue({
el: '#app',
data: {
message: 'Runoob!'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
cnt+=1;
return cnt+this.message.split('').reverse().join('')
}
},
methods: {
reversedMessage2: function () {
cnt+=1;
return cnt+this.message.split('').reverse().join('')
}
}
})

Vue.js 监听属性

本章节,我们将为大家介绍 Vue.js 监听属性 watch,我们可以通过 watch 来响应数据的变化。

以下实例通过使用 watch 实现计数器:

1
2
3
4
<div>
<p style="font-size:25px;">计数器: {{ counter }}</p>
<button @click="counter++" style="font-size:25px;">点我</button>
</div>
1
2
3
4
5
6
7
8
9
10
var vm = new Vue({
//*******************
data: {
counter: 1
//*******************
});
// F12 打开开发者模式控制台观察效果
vm.$watch('counter', function(nval, oval) {
console.log('计数器值的变化 :' + oval + ' 变为 ' + nval + '!');
});

千米和米的换算

1
2
3
4
5
<br>
<div>
千米 : <input type="text" v-model="kilometers">
米 : <input type="text" v-model="meters">
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
data: {
//*************
kilometers : 0,
meters:0
},
watch : {
kilometers: function (newVal, oldVal) {
console.log('newVal=' + newVal + ' oldVal=' + oldVal)
this.kilometers = newVal
this.meters = this.kilometers * 1000
},
meters: function (newVal) {
this.kilometers = newVal / 1000
this.meters = newVal
}
}

控制台为什么会打印两次呢?因为kilometers改变执行一次,并且改变了 meters,所以执行的meters改变(又改变了kilometers),所以又打印了一次,但因为 meters在此时并没有改变,所以没有后续触发。

Vue.js 样式绑定

Vue.js class

class 与 style 是 HTML 元素的属性,用于设置元素的样式,我们可以用 v-bind 来设置样式属性。

Vue.js v-bind 在处理 class 和 style 时, 专门增强了它。表达式的结果类型除了字符串之外,还可以是对象或数组。

class 属性绑定

我们可以为 v-bind:class 设置一个对象,从而动态的切换 class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>vue_maxzhao</title>
<style>
.class1 {
background: #444;
color: #eee;
}
.active {
background: #42b983;
}
.text-danger {
background: red;
}
</style>
</head>
<!-- ***************** -->
<!-- ***************** -->
<div v-bind:class="{ active: isActive }"></div>
<!-- 多个 -->
<div class="active"></div>
<div :class="{ active: isActive , 'text-danger': true }">
BBBBBBBBBBBBB
</div>
<!-- 对象 -->
<div :class="classObj">
AAAAAAAAAAAAAAAAAAAAAAAAAAA
</div>
<!-- 数组 , 等同于 class="active text-danger"-->
<div :class="[activeClass, errorClass]">
AAAAAAAAAAAAAAAAAAAAAAAAAAA
</div>
<!-- 数组中添加表达式-->
<div :class="[activeClass, true?errorClass:'']">C</div>
<div :class="[activeClass, false?errorClass:'']">D</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
data: {
//**********************
isActive: true,
activeClass: 'active',
errorClass: 'text-danger'
}
computed: {
//**********************
classObj: function () {
return {
active: true,
'text-danger': true
}
}
}

Vue.js style(内联样式)

这里的所有操作就不写在 js 中了,表达式等都是可以直接写在页面上的。

1
2
3
4
5
6
7
8
9
10
11
<br>
<!-- v-bind 的缩写还记得吧? ":"哦 -->
<div :style="{ color: 'green', fontSize: 30 + 'px' }">
maxzhao_style——普通绑定,'green'、30 为值
</div>
<div :style="{color: 'green',fontSize: '30px'}">
maxzhao_style——对象绑定,{color: 'green',fontSize: '30px'} 是对象
</div>
<div :style="[{color: 'green',fontSize: '30px'},{'font-weight': 'bold'}]">
maxzhao_style——数组对象绑定,[{color: 'green',fontSize: '30px'},{'font-weight': 'bold'}] 是数组对象绑定
</div>

Vue.js 事件处理器

v-on

事件监听可以使用 v-on 指令。

通常情况下,我们需要使用一个方法来调用 JavaScript 方法。

v-on 可以接收一个定义的方法来调用。

1
2
3
4
5
<br>
<!-- `greet` 是在下面定义的方法名 -->
<button v-on:click="greet">Greet</button>
<button @click="say('hi')">Say hi</button>
<button @click="say('what')">Say what</button>

js 中添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
methods: {
greet: function (event) {
// `this` 在方法里指当前 Vue 实例
console.log('Hello ' + this.author + '!')
// `event` 是原生 DOM 事件
if (event) {
console.log(event.target.tagName)
}
},
say: function (message) {
console.log(message)
}
}
// 还可以在最外层添加 vm.greet()来调用当前方法,直接调用的时候,event并不存在,因为没有点击事件

事件修饰符

Vue.js 为 v-on 提供了事件修饰符来处理 DOM 事件细节,如:event.preventDefault() 或 event.stopPropagation()。

Vue.js通过由点(.)表示的指令后缀来调用修饰符。

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<br>
<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis">click.stop</a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit">submit.prevent</form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat">click.stop.prevent</a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent>submit.prevent</form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>
<!-- click 事件只能点击一次,2.1.4版本新增 -->
<a v-on:click.once="doThis"></a>
<br>

按键修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 需要js methods
methods: {
clear: function (event) {
console.log('------ clear ----------')
if (event) {
console.log(event.target.tagName)
}
},
submit: function (event) {
console.log('------ submit ----------')
if (event) {
console.log(event.target.tagName)
console.log(event.keyCode)//输出当前key code的值
}
},
doSomething: function (event) {
console.log('------ doSomething ----------')
if (event) {
console.log(event.target.tagName)
console.log(event.keyCode)//输出当前ctrlKey 的bool值
}
}
//*****************************

Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

1
2
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input @keyup.13="submit" value="keyup = 13 ">

记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:

1
2
3
4
5
6
7
8
9
<!-- 同上 -->
<input v-on:keyup.enter="submit" value="keyup = 13 ">
<!-- 缩写语法 -->
<input @keyup.enter="submit" value="enter">
<!-- Alt + C -->
<input @keyup.alt.67="clear" value="Alt + C">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Ctrl + Click will Do something</div>
<br>

全部的按键别名:

  • .enter
  • .tab
  • .delete (捕获 “删除” 和 “退格” 键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right
  • .ctrl
  • .alt
  • .shift
  • .meta

Vue.js 表单

可以用 v-model 指令在表单控件元素上创建双向数据绑定。

v-model 会根据控件类型自动选取正确的方法来更新元素。\

常规表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  <p>input 元素:</p>
<input v-model="author" placeholder="编辑我……">
<p>author: {{ author }}</p>

<p>textarea 元素:</p>
<p style="white-space: pre">{{ message }}</p>
<textarea v-model="message" placeholder="多行文本输入……">
</textarea>
<br>
<!-- for 后面加 checkbox或radio 的id,点击会选中或取消选中 -->
<p>单个复选框:</p>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
<p>多个复选框:</p>
<input type="checkbox" id="runoob" value="Runoob" v-model="checkedNames">
<label for="runoob">Runoob</label>
<input type="checkbox" id="google" value="Google" v-model="checkedNames">
<label for="google">Google</label>
<input type="checkbox" id="taobao" value="Taobao" v-model="checkedNames">
<label for="taobao">taobao</label>
<br>
<span>选择的值为: {{ checkedNames }}</span>
<br>
<div>
<input type="radio" id="cat" value="cat" v-model="picked">
<label for="cat">cat</label>
<br>
<input type="radio" id="dog" value="dog" v-model="picked">
<label for="google">dog</label>
<br>
<span>选中值为: {{ picked }}</span>
</div>
<br>
<select v-model="selected" name="fruit">
<option value="">选择一个网站</option>
<option value="www.baidu.com">baidu</option>
<option value="www.google.com">Google</option>
</select>

<div id="output">
选择的网站是: {{selected}}
</div>
1
2
3
4
checked : false,
checkedNames: [],
picked: 'cat',
selected: ''

修饰符

.lazy

在默认情况下, v-model 在 input 事件中同步输入框的值与数据,但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步:

1
2
<!-- 在 "change" 而不是 "input" 事件中更新 -->
<input v-model.lazy="msg" >

.number

如果想自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值),可以添加一个修饰符 number 给 v-model 来处理输入值:

1
<input v-model.number="age" type="number">

这通常很有用,因为在 type=”number” 时 HTML 中输入的值也总是会返回字符串类型。

.trim

如果要自动过滤用户输入的首尾空格,可以添加 trim 修饰符到 v-model 上过滤输入:

1
<input v-model.trim="msg">

Vue.js 组件component

组件(Component)是 Vue.js 最强大的功能之一。

组件可以扩展 HTML 元素,封装可重用的代码。

组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:

img

全局组件

注册一个全局组件语法格式如下:

1
Vue.component(tagName, options)

tagName 为组件名,options 为配置选项。注册后,我们可以使用以下方式来调用组件:

1
<tagName></tagName>

实例

1
2
3
4
5
6
7
8
9
10
11
12

<script>
// 注册,要在创建实例之前完成
Vue.component('maxzhao_all', {
template: '<h1>自定义组件 maxzhao_all !</h1>'
})
// 创建根实例
new Vue({
el: '#app'
})
</script>
<maxzhao_all></maxzhao_all>

局部组件

我们也可以在实例选项中注册局部组件,这样组件只能在这个实例中使用:

1
2
3
<div id="vue_det">
<maxzhao></maxzhao>
<maxzhao_all></maxzhao_all>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 注册
Vue.component('maxzhao_all', {
template: '<h1>自定义组件 maxzhao_all ! </h1>'
})
var Child = {
// eslint-disable-next-line no-tabs
template: '<h1>自定义组件 maxzhao !</h1>'
}
// eslint-disable-next-line no-unused-vars
var vm = new Vue({
el: '#vue_det',
components: {
// <maxzhao> 将只在父模板可用
'maxzhao': Child
}

Prop

prop 是父组件用来传递数据的一个自定义属性。

父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 “prop”:

1
<maxzhao_all message1="hello!"></maxzhao_all>
1
2
3
4
5
6
// 注册
Vue.component('maxzhao_all', {
// 声明 props
props: ['message1'],
template: '<h1>自定义组件 maxzhao_all params:{{message1}} ! </h1>'
})

首次接触可能会很难理解,其实很简单,就像<div class="class1" ></div> 一样,为当前的divclass 的传递一个 class1的参数。

动态 Prop

1
<maxzhao_all v-bind:message1="author"></maxzhao_all><maxzhao_all :message1="author"></maxzhao_all>
1
2
3
data: {
author: 'maxzhao'
}

Prop 实例

1
2
3
4
5
6
7
8
9
10
11
12
13
// 当前列表中的一条记录
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.name }}</li>'
})
//***************
data: {
sites: [
{ name: 'Runoob' },
{ name: 'Google' },
{ name: 'Taobao' }
]
}
1
2
3
<ol>
<todo-item v-for="item in sites" v-bind:todo="item"></todo-item>
</ol>

Prop 验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Vue.component('example', {
props: {
// 基础类型检测 (`null` 意思是任何类型都可以)
propA: Number,
// 多种类型
propB: [String, Number],
// 必传且是字符串
propC: {
type: String,
required: true
},
// 数字,有默认值
propD: {
type: Number,
default: 100
},
// 数组/对象的默认值应当由一个工厂函数返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
}
}
})

type 可以是下面原生构造器:

  • String
  • Number
  • Boolean
  • Function
  • Object
  • Array

type 也可以是一个自定义构造器,使用 instanceof 检测。

自定义事件

父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,就需要使用自定义事件!

我们可以使用 v-on 绑定自定义事件, 每个 Vue 实例都实现了事件接口(Events interface),即:

  • 使用 $on(eventName) 监听事件
  • 使用 $emit(eventName) 触发事件

另外,父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。

以下实例中子组件已经和它外部完全解耦了。它所做的只是触发一个父组件关心的内部事件。

1
2
3
4
5
6
<div id="counter-event-example">
<p>{{ total }}</p>
<!-- 监听自定义的事件-->
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Vue.component('button-counter', {
template: '<button v-on:click="incrementHandler">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
incrementHandler: function () {
this.counter += 1
this.$emit('increment')
}
}
})
var vm = new Vue({
el: '#vue_det',
data: {
//****************
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}

这个可能需要解释一下(自己的理解),首先加载当前的button按钮,当前按钮的click执行组件的incrementHandler,然后执行当前组件在html中的click事件,为什么要反向执行呢?因为页面中绑定的事件是 当前vue_det下的事件。

如果你想在某个组件的根元素上监听一个原生事件。可以使用 .native 修饰 v-on 。例如:

1
<my-component v-on:click.native="doTheThing"></my-component>

data 必须是一个函数

上面例子中,可以看到 button-counter 组件中的 data 不是一个对象,而是一个函数:

1
2
3
4
5
data: function () {
return {
count: 0
}
}

这样的好处就是每个实例可以维护一份被返回对象的独立的拷贝,如果 data 是一个对象则会影响到其他实例,如下所示(没有尝试,js基础知识):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<div id="components-demo3" class="demo">
<button-counter2></button-counter2>
<button-counter2></button-counter2>
<button-counter2></button-counter2>
</div>

<script>
var buttonCounter2Data = {
count: 0
}
Vue.component('button-counter2', {
/*
data: function () {
// data 选项是一个函数,组件不相互影响
return {
count: 0
}
},
*/
data: function () {
// data 选项是一个对象,会影响到其他实例
return buttonCounter2Data
},
template: '<button v-on:click="count++">点击了 {{ count }} 次。</button>'
})
new Vue({ el: '#components-demo3' })
</script>

Vue.js 自定义指令

除了默认设置的核心指令( v-model 和 v-show ), Vue 也允许注册自定义指令。

下面我们注册一个全局指令 v-focus, 该指令的功能是在页面加载时,元素获得焦点:

全局指令:实例 v-focus

1
2
<p>页面载入时,input 元素自动获取焦点:</p>
<input v-focus>
1
2
3
4
5
6
7
8
// 注册一个全局自定义指令 v-focus
Vue.directive('focus', {
// 当绑定元素插入到 DOM 中。
inserted: function (el) {
// 聚焦元素
el.focus()
}
})

我们也可以在实例使用 directives 选项来注册局部指令,这样指令只能在这个实例中使用:

局部指令:实例 v-focus

把上面的代码注释掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
var vm = new Vue({
el: '#vue_det',
directives: {
// 注册一个局部的自定义指令 v-focus
focus: {
// 指令的定义
inserted: function (el) {
// 聚焦元素
el.focus()
}
}
}
})

钩子

钩子函数

指令定义函数提供了几个钩子函数(可选):

  • bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
  • inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
  • update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新(详细的钩子函数参数见下)。
  • componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
  • unbind: 只调用一次, 指令与元素解绑时调用。

钩子函数参数

钩子函数的参数有:

  • el: 指令所绑定的元素,可以用来直接操作 DOM 。
  • binding: 一个对象,包含以下属性:
    • name: 指令名,不包括 v- 前缀。
    • value: 指令的绑定值, 例如: v-my-directive="1 + 1", value 的值是 2
    • oldValue: 指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用。无论值是否改变都可用。
    • expression: 绑定值的表达式或变量名。 例如 v-my-directive="1 + 1" , expression 的值是 "1 + 1"
    • arg: 传给指令的参数。例如 v-my-directive:foo, arg 的值是 "foo"
    • modifiers: 一个包含修饰符的对象。 例如: v-my-directive.foo.bar, 修饰符对象 modifiers 的值是 { foo: true, bar: true }
  • vnode: Vue 编译生成的虚拟节点。
  • oldVnode: 上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用。

以下实例演示了这些参数的使用:

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
// 注册一个全局自定义指令 v-focus
Vue.directive('maxzhao', {
bind: function (el, binding, vnode) {
var s = JSON.stringify
el.innerHTML =
'name: ' + s(binding.name) + '<br>' +
'value: ' + s(binding.value) + '<br>' +
'expression: ' + s(binding.expression) + '<br>' +
'argument: ' + s(binding.arg) + '<br>' +
'modifiers: ' + s(binding.modifiers) + '<br>' +
'vnode keys: ' + Object.keys(vnode).join(', ')
}
})

有时候我们不需要其他钩子函数,我们可以简写函数,如下格式:

1
<div v-maxzhao_dir:a.b.c='message' v-maxzhao_color="{ color: 'green', text: 'maxzhao' }" ></div>
1
2
3
4
Vue.directive('maxzhao_color', function (el, binding) {
// 设置指令的背景颜色
el.style.backgroundColor = binding.value.color
})

指令函数可接受所有合法的 JavaScript 表达式,以下实例传入了 JavaScript 对象:

1
2
3
4
5
Vue.directive('maxzhao_color', function (el, binding) {
// 简写方式设置文本及背景颜色
el.innerHTML = binding.value.text
el.style.backgroundColor = binding.value.color
})

Vue.js 路由(终于到了激动人心,阔步前进的时刻)

本章节我们将为大家介绍 Vue.js 路由。

Vue.js 路由允许我们通过不同的 URL 访问不同的内容。

通过 Vue.js 可以实现多视图的单页Web应用(single page web application,SPA)。

Vue.js 路由需要载入 vue-router 库

中文文档地址:vue-router文档

打住:如果是按照本文的安装方式,则默认已经安装了路由


安装

1、直接下载 / CDN

1
wget https://unpkg.com/vue-router/dist/vue-router.js

2、NPM

推荐使用淘宝镜像:

1
cnpm install vue-router

简单实例

Vue.js + vue-router 可以很简单的实现单页应用。

是一个组件,该组件用于设置一个导航链接,切换不同 HTML 内容。 to 属性为目标地址, 即要显示的内容。

以下实例中我们将 vue-router 加进来,然后配置组件和路由映射,再告诉 vue-router 在哪里渲染它们。

HTML代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app-router-demo">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>

JavaScript 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 0. 如果使用模块化机制编程,导入 Vue 和 VueRouter,要调用 Vue.use(VueRouter)

// 1. 定义(路由)组件。
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }

// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]

// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes // (缩写)相当于 routes: routes
})

// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
router
}).$mount('#app-router-demo')

// 现在,应用已经启动了!

打眼一看,怎么还是和安装的不一样?这时候的我也是一脸懵逼。先尝试一下这个。最终还是不行,因为new VueRouter这个是直接报错的。采用当前的方式引入:

1
2
3
4
5
6
7
8
9
10
11
12
import VueRouter from 'vue-router'
// demo
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})

接下来我把教程的所有代码都删掉了,直接看脚手架的源码。

直接上手脚手架

为了方便阅读,把router/index.js改为router/indexRouter.js,此时,会有 test/unit/jest.conf.js中的 js 路径修改。

当我想把indexRouter.js移动到components目录下的时候,发现界面报错了。

1
2
3
4
5
6
7
 ERROR  Failed to compile with 2 errors   
These dependencies were not found:

* vue-router/types in ./src/router/indexRouter.js
* vue/types in ./src/router/indexRouter.js

To install them, you can run: npm install --save vue-router/types vue/types

我就按照提示进行了安装

1
2
3
npm install --save vue-router/types vue/types
# 报错 找不到 赶紧
npm i 一下

提示:此处有一个build/webpack.base.conf.js文件下的

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
// ************************
resolve: {
// 在 import 的可以省略文件的后缀名
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
// 此处设置@ 代表src目录,等同于‘/’
'@': resolve('src'),
}
},

提示: /home/maxzhao/code/vue/vue_maxzhao/src/router/index.js文件下

1
2
3
4
5
6
7
8
9
10
// exxport default 只能导出一个对象,直接引入就可以使用
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})

导出多个对象

1
2
3
4
export const str = 'hello world';
export function f(a){ return a+1;}
// 在其它文件中引入
import { str, f } from 'demo1'

实践

src/router/index.js中插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import HelloMaxzhao from '@/components/HelloMaxzhao'

Vue.use(Router)

export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/hello_maxzhao',
name: 'HelloMaxzhao',
component: HelloMaxzhao
}
]
})

新建src/components/HelloMaxzhao.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h2>author:{{author}}</h2>
</div>
</template>

<script>
export default {
name: 'HelloMaxzhao',
data () {
return {
msg: 'Hello MaxZhao',
author: 'maxzhao'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
</style>

浏览器访问:http://localhost:8080/#/hello_maxzhao

点击过的导航链接都会加上样式 **class =”router-link-exact-active router-link-active”**。


接下来我们可以了解下更多关于 的属性。

to

表示目标路由的链接。 当被点击后,内部会立刻把 to 的值传到 router.push(),所以这个值可以是一个字符串或者是描述目标位置的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<router-link to="/hello_maxzhao" >to /hello_maxzhao</router-link><br>
<!-- 字符串 -->
<router-link to="hello_maxzhao">hello_maxzhao</router-link><br>
<!-- 渲染结果 -->
<a href="#/hello_maxzhao">a href=hello_maxzhao</a><br>
<!-- 使用 v-bind 的 JS 表达式 -->
<router-link v-bind:to="'hello_maxzhao'">v-bind:to hello_maxzhao</router-link><br>
<!-- 不写 v-bind 也可以,就像绑定别的属性一样 -->
<router-link :to="'hello_maxzhao'">:to hello_maxzhao</router-link><br>
<!-- 同上 -->
<router-link :to="{ path: 'hello_maxzhao' }">:to="{ path: 'hello_maxzhao' }"</router-link><br>
<!-- 命名的路由 -->
<router-link :to="{ name: 'HelloMaxzhao', params: { userId: 123 }}">命名的路由 HelloMaxzhao</router-link><br>
<!-- 带查询参数,下面的结果为 /hello_maxzhao?plan=private -->
<router-link :to="{ path: 'hello_maxzhao', query: { plan: 'private' }}">带查询参数 hello_maxzhao</router-link>

replace

设置 replace 属性的话,当点击时,会调用 router.replace() 而不是 router.push(),导航后不会留下 history 记录。

1
<router-link :to="{ path: '/abc'}" replace></router-link>

append

设置 append 属性后,则在当前 (相对) 路径前添加基路径。例如,我们从 /a 导航到一个相对路径 b,如果没有配置 append,则路径为 /b,如果配了,则为 /a/b

1
<router-link :to="{ path: 'relative/path'}" append></router-link>

tag

有时候想要 <router-link> 渲染成某种标签,例如 <li>。 于是我们使用 tag prop 类指定何种标签,同样它还是会监听点击,触发导航。

1
2
3
<router-link to="/foo" tag="li">foo</router-link>
<!-- 渲染结果 -->
<li>foo</li>

active-class

设置 链接激活时使用的 CSS 类名。可以通过以下代码来替代。

1
2
3
4
5
6
7
8
9
<style>
._active{
background-color : red;
}
</style>
<p>
<router-link v-bind:to = "{ path: '/route1'}" active-class = "_active">Router Link 1</router-link>
<router-link v-bind:to = "{ path: '/route2'}" tag = "span">Router Link 2</router-link>
</p>

注意这里 class 使用 **active_class=”_active”**。

exact-active-class

配置当链接被精确匹配的时候应该激活的 class。可以通过以下代码来替代。

1
2
3
4
<p>
<router-link v-bind:to = "{ path: '/route1'}" exact-active-class = "_active">Router Link 1</router-link>
<router-link v-bind:to = "{ path: '/route2'}" tag = "span">Router Link 2</router-link>
</p>

router-link 默认情况下的路由是模糊匹配,例如当前路径是 /article/1 那么也会激活 ,所以当设置 exact-active-class 以后,这个 router-link 只有在当前路由被全包含匹配时才会被激活 exact-active-class 中的 class,例如:

1
<router-link to="/article" active-class="router-active"></router-link>

当用户访问 /article/1 时会被激活为:

1
<a href="#/article" class="router-active" rel="nofollow"></a>

而当使用:

1
<router-link to="/article" exact-active-class="router-active"></router-link>

当用户访问 /article/1 时,不会激活这个 link 的 class:

1
<a href="#/article" rel="nofollow"></a>

event

声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组。

1
<router-link v-bind:to = "{ path: '/route1'}" event = "mouseover">Router Link 1</router-link>

以上代码设置了 event 为 mouseover ,及在鼠标移动到 Router Link 1 上时导航的 HTML 内容会发生改变。

NPM 路由实例

接下来我们演示了一个使用 npm 简单的路由实例,开始前,请先下载该实例源代码:

路由实例

你也可以在 Github 上下载:https://github.com/chrisvfritz/vue-2.0-simple-routing-example

下载完后,解压该目录,重命名目录为 vue-demo,vu 并进入该目录,执行以下命令:

1
2
3
4
5
# 安装依赖,使用淘宝资源命令 cnpm
cnpm install

# 启动应用,地址为 localhost:8080
cnpm run dev

如果你需要发布到正式环境可以执行以下命令:

1
cnpm run build

执行成功后,访问 http://localhost:8080 即可看到如下界面:

img

beforeEach 跳转路由前

1
2
3
router.beforeEach((to, from, next) => {
// 做一些操作,比如判断当前用户 token 是否过期
});
  • to: Route: 即将要进入的目标 路由对象

  • from: Route: 当前导航正要离开的路由

  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

    • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
    • next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
    • next(‘/‘) 或者 next({ path: ‘/‘ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
    • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

afterEach 跳转路由后

1
2
3
router.afterEach(() => {
NProgress.done(); // 结束Progress
});

Vue.js 混入

混入 (mixins)定义了一部分可复用的方法或者计算属性。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。

来看一个简单的实例:

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义一个混入对象
var myMixin = {
created: function () {
this.startmixin()
},
methods: {
startmixin: function () {
document.write('欢迎来到混入实例')
}
}
}
var Component1 = Vue.extend({
mixins: [myMixin]
})
new Component1()

选项合并

当组件和混入对象含有同名选项时,这些选项将以恰当的方式混合。

比如,数据对象在内部会进行浅合并 (一层属性深度),在和组件的数据发生冲突时以组件数据优先

以下实例中,Vue 实例与混入对象包含了相同的方法。从输出结果可以看出两个选项合并了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var mixin = {
created: function () {
document.write('混入调用' + '<br>')
}
}
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: {App},
template: '<App/>',
mixins: [mixin],
//created 参数
created: function () {
document.write('组件调用' + '<br>')
}
})

输出结果为:

1
2
混入调用
组件调用

如果 methods 选项中有相同的函数名,则 Vue 实例优先级会较高。如下实例,Vue 实例与混入对象的 methods 选项都包含了相同的函数:

相同的函数名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var mixin = {
methods: {
hellworld: function () {
document.write('HelloWorld 方法' + '<br>');
},
samemethod: function () {
document.write('Mixin:相同方法名' + '<br>');
}
}
}
/* eslint-disable no-new */
var vm = new Vue({
el: '#app',
router,
components: {App},
template: '<App/>',
mixins: [mixin],
methods: {
start: function () {
document.write('start 方法' + '<br>');
},
samemethod: function () {
document.write('Main:相同方法名' + '<br>');
}
}
})
vm.hellworld();
vm.start();
vm.samemethod();

输出结果为:

1
2
3
HelloWorld 方法
start 方法
Main:相同方法名

以上实例,我们调用了以下三个方法:

1
2
3
vm.hellworld();
vm.start();
vm.samemethod();

从输出结果 methods 选项中如果碰到相同的函数名则 Vue 实例有更高的优先级会执行输出。并且全局方法不执行。


全局混入

也可以全局注册混入对象。注意使用! 一旦使用全局混入对象,将会影响到 所有 之后创建的 Vue 实例。使用恰当时,可以为自定义对象注入处理逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
created: function () {
var myOption = this.$options.myOption;
// this.$options 可以获取当前vue对象
if (myOption) {
console.log(this.$options)
document.write(myOption)
}
}
})
/* eslint-disable no-new */
var vm = new Vue({
el: '#app',
router,
components: {App},
template: '<App/>',
myOption: 'hello!'
})

谨慎使用全局混入对象,因为会影响到每个单独创建的 Vue 实例 (包括第三方模板)。

Vue.js Ajax(vue-resource)

Vue 要实现异步加载需要使用到 vue-resource 库。

1
<script src="https://cdn.staticfile.org/vue-resource/1.5.1/vue-resource.min.js"></script>

Get 请求

以下是一个简单的 Get 请求实例,请求地址是一个简单的 txt 文本:

如果需要传递数据,可以使用 this.$http.get(‘get.php’,{params : jsonData}) 格式,第二个参数 jsonData 就是传到后端的数据。

1
2
3
4
5
this.$http.get('get.php',{params : {a:1,b:2}}).then(function(res){
document.write(res.body);
},function(res){
console.log(res.status);
});

post 请求

post 发送数据到后端,需要第三个参数 **{emulateJSON:true}**。

emulateJSON 的作用: 如果Web服务器无法处理编码为 application/json 的请求,你可以启用 emulateJSON 选项。

demo_test_post.php 代码如下:

1
2
3
4
5
6
7
<?php
$name = isset($_POST['name']) ? htmlspecialchars($_POST['name']) : '';
$city = isset($_POST['url']) ? htmlspecialchars($_POST['url']) : '';
echo '网站名: ' . $name;
echo "\n";
echo 'URL 地址: ' .$city;
?>

语法 & API

你可以使用全局对象方式 Vue.http 或者在一个 Vue 实例的内部使用 this.$http来发起 HTTP 请求。

1
2
3
4
5
6
7
// 基于全局Vue对象使用http
Vue.http.get('/someUrl', [options]).then(successCallback, errorCallback);
Vue.http.post('/someUrl', [body], [options]).then(successCallback, errorCallback);

// 在一个Vue实例内使用$http
this.$http.get('/someUrl', [options]).then(successCallback, errorCallback);
this.$http.post('/someUrl', [body], [options]).then(successCallback, errorCallback);

vue-resource 提供了 7 种请求 API(REST 风格):

1
2
3
4
5
6
7
get(url, [options])
head(url, [options])
delete(url, [options])
jsonp(url, [options])
post(url, [body], [options])
put(url, [body], [options])
patch(url, [body], [options])

除了 jsonp 以外,另外 6 种的 API 名称是标准的 HTTP 方法。

options 参数说明:

参数 类型 描述
url string 请求的目标URL
body Object, FormData, string 作为请求体发送的数据
headers Object 作为请求头部发送的头部对象
params Object 作为URL参数的参数对象
method string HTTP方法 (例如GET,POST,…)
timeout number 请求超时(单位:毫秒) (0表示永不超时)
before function(request) 在请求发送之前修改请求的回调函数
progress function(event) 用于处理上传进度的回调函数 ProgressEvent
credentials boolean 是否需要出示用于跨站点请求的凭据
emulateHTTP boolean 是否需要通过设置X-HTTP-Method-Override头部并且以传统POST方式发送PUT,PATCH和DELETE请求。
emulateJSON boolean 设置请求体的类型为application/x-www-form-urlencoded

通过如下属性和方法处理一个请求获取到的响应对象:

属性 类型 描述
url string 响应的 URL 源
body Object, Blob, string 响应体数据
headers Header 请求头部对象
ok boolean 当 HTTP 响应码为 200 到 299 之间的数值时该值为 true
status number HTTP 响应码
statusText string HTTP 响应状态
方法 类型 描述
text() 约定值 以字符串方式返回响应体
json() 约定值 以格式化后的 json 对象方式返回响应体
blob() 约定值 以二进制 Blob 对象方式返回响应体

axios(听说比vueresource更好)

基于promise用于浏览器和node.js的http客户端

特点

  • 支持浏览器和node.js
  • 支持promise
  • 能拦截请求和响应
  • 能转换请求和响应数据
  • 能取消请求
  • 自动转换JSON数据
  • 浏览器端支持防止CSRF(跨站请求伪造)

安装

npm安装

1
$ npm install axios

bower安装

1
$ bower install axios

通过cdn引入

1
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

例子

发起一个GET请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Make a request for a user with a given ID
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

// Optionally the request above could also be done as
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

发起一个POST请求

1
2
3
4
5
6
7
8
9
10
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

同时发起多个请求

1
2
3
4
5
6
7
8
9
10
11
12
function getUserAccount() {
return axios.get('/user/12345');
}

function getUserPermissions() {
return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));

官方地址:http://www.axios-js.com/zh-cn/docs/

其它插件

NProgress 进度条

1
2
3
4
NProgress.start() // —start 
NProgress.set(0.4)// — 将进度设置到具体的百分比位置
NProgress.inc() // — 少量增加进度
NProgress.done()// — 将进度条标为完成状态

vuedraggable 拖动

vue-count-to 数字滚动

showdown 使页面支持 markdown

Mock.mock() 拦截请求响应数据

参考

问题

Failed to mount component: template or render function not defined.

1
2
3
4
5
6
7
[Vue warn]: Failed to mount component: template or render function not defined.

found in

---> <Anonymous>
<App> at src/App.vue
<Root>

因为 vue 版本高了,需要在 require 之后 加上 default, 来指定文件中的 export default

1
2
Try adding .default to the end of your require().
component: require('./index/index.vue').default

You cannot set a form field before rendering a field associated with the value

只能少传参,不能多传、错传参数。

1
2
3
let thisDetail = pick(that.detail, 'money',
'goodsName', 'remark', 'delStatus', 'expenseTime');
that.form.setFieldsValue(thisDetail);

本文地址: https://github.com/maxzhao-it/blog/post/36127/

Spring中正则获取包下的类

1
2
3
4
5
6
7
8
ResourcePatternResolver resourcePatternResolve=new PathMatchingResourcePatternResolver();
// 获取 gt包下的所有类,我想类名应该没有特殊字符吧
resourcePatternResolve.getResources("classpath*:gt/**/*.class");
// 会获取到jdk中的类
resourcePatternResolve.getResources("classpath*:/**/*.class");
// 不会获取到jdk中的类
resourcePatternResolve.getResources("classpath:/**/*.class");

本文地址: https://github.com/maxzhao-it/blog/post/19701/

关于JDK环境变量,我推荐一下这个

前言

最近开发遇到java -jar xxx.jar 启动的项目没有办法关闭的问题。

window下查找pid有些繁琐,在寻求更好骚操作的途中,我遇到了JDKJPS

jpsjava 命令一样,是在jdk/bin目录下的命令。然后我就去探索了一下。

JPS 简介

JPS 名称:

1
jps - Java Virtual Machine Process Status Tool

命令用法:

1
2
3
4
jps [options] [hostid]
options:命令选项,用来对输出格式进行控制
hostid:指定特定主机,可以是ip地址和域名, 也可以指定具体协议,端口。
[protocol:][[//]hostname][:port][/servername]

功能描述:

  • jps 是用于查看有权访问的 hotspot 虚拟机的进程. 当未指定 hostid 时,默认查看本机 jvm 进程;

  • 查看指定hostid 机器上的 jvm 进程,此时 hostid 所指机器必须开启 jstatd 服务。 jps
    可以列出 jvm 进程 lvmid ,主类类名,main 函数参数, jvm 参数,jar 名称等信息。

  • jps 命令类似与 linuxps 命令,但是它只列出系统中所有的 Java 应用程序。 通过 jps 命令可以方便地查看 Java 进程的启动类、传入参数和 Java 虚拟机参数等信息。

  • 在 linux 中查看 Java 的进程,一般ps -ef | grep java来获取进程ID。只获取 Java 程序的进程,可以直接使用 jps 命令。window
    也是一样的,杀死的命令变为了taskkill -f /pid xxx

命令选项及功能:

  • -q:只输出进程 ID
  • -m:输出传入 main 方法的参数
  • -l:输出完全的包名,应用主类名,jar的完全路径名
  • -v:输出jvm参数
  • -V:输出通过flag文件传递到JVM中的参数

没添加option的时候,默认列出VM标示符号和简单的class或jar名称.如下:

-q :仅仅显示VM 标示,不显示jar,class, main参数等信息.

-m:输出主函数传入的参数. 下的hello 就是在执行程序时从命令行输入的参数

-l: 输出应用程序主类完整package名称或jar完整名称.

-v: 列出jvm参数, -Xms20m -Xmx50m是启动程序指定的jvm参数

-V: 输出通过.hotsportrc或-XX:Flags=指定的jvm参数

-Joption:传递参数到javac 调用的java lancher.

本文地址:https://www.jianshu.com/p/37bfacccf3e2

本文地址: https://github.com/maxzhao-it/blog/post/37965/

###TIP

起初,我写一个循环list的stram流,list.stram().filter(item->item>0).map(item->item.get(”id“)).parallel().collect(Collectors.toList)
的时候,想用并行流去处理,但是不了解filter与map并行,还是filter与map内部并行。

一个顺序执行的流转变成一个并行的流只要调用 parallel()方法,一个自定义list的例子:
return Stream.iterate(1L, i -> i +1).limit(n).parallel().reduce(0L,Long::sum);

一个并发流转成顺序的流只要调用sequential()方法
stream.parallel() .filter(…) .sequential() .map(…) .parallel() .reduce();
这两个方法可以多次调用, 只有最后一个调用决定这个流是顺序的还是并发的。

本文地址 Stream多线程并行数据处理

本文地址: https://github.com/maxzhao-it/blog/post/5456/