首页 文章 精选 留言 我的

精选列表

搜索[快速入门],共10006篇文章
优秀的个人博客,低调大师

CentOS7,8上快速安装Gitea,搭建Git服务器

安装准备 1.安装Git环境 sh 复制代码 yum -y install git 2.下载Gitea运行程序 下载地址:https://dl.gitea.io/gitea/ 注:下载gitea-[版本号]-linux-amd64的运行程序 安装 1.将安装包上传至指定目录 如:/data0/gitea/gitea-linux-amd64 2.重命名安装包 sh 复制代码 mv /data0/gitea/gitea-linux-amd64 /data0/gitea/gitea 3.修改运行权限 sh 复制代码 chmod +x /data0/gitea/gitea 4.直接运行 sh 复制代码...

优秀的个人博客,低调大师

Docker快速安装Oracle11G,搭建oracle11g学习环境

安装说明 1.操作系统CentOS7_x64 2.安装的数据库为Oracle11G 3.已经安装了Docker环境 4.安装一些必要的软件 sh 复制代码 yum install unzip -y unzip:解压oracle安装文件 5.提前准备Oracle11G安装镜像 下载地址:https://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html 6.需要检查是否有swap分区,如果没有请设置 安装 1.镜像准备 将下载的Oracle安装包上传到指定目录并解压 如:/data0/oracle...

优秀的个人博客,低调大师

Python快速入门_1

注释 # 用#号字符开头注释单行 """ 三个引号可以注释多行 三个引号可以注释多行 三个引号可以注释多行 """ 原始数据类型和运算符 (1)整型 #整数 3 #=>3 (2)算术运算 #加法 1+1 #=>2 #减法 8-1 #=>7 #乘法 10*2 #=>20 #除法 !!!结果自动转换成浮点数 35/5 #=>7.0 5/3 #=>1.6666666666666667 #整数除法 !!!结果向下取整 5//3 #=>1 5.0//3.0 #=>1.0 -5//3 #=>-2 5//(-3) #=>-2 -5.0//3.0 #=>-2.0 #浮点数的运算结果也是浮点数 3*2.0 #=>6.0 #取模 7%3 #=>1 #x的y次方 2**4 #=>16 #用括号决定优先级 (1+3)*2 #=>8 (2)布尔运算与比较运算 #布尔值 True #=>True False #=>False #用not取非 not True #=>False not False #=>True #逻辑运算符,注意and和or都是小写 True and False #=>False True or False #=>True #整数也可以当做布尔值 0== False #=>True 2==True #=>False 1==True #=>True #用==判断相等 1==1 #=>True 2==1 #=>False #用!=判断不等 1!=1 #=>False 1!=2 #=>True #比较大小 1<10 #=>True 2<=2 #=>True 2>=2 #=>True (4)字符串运算 #字符串用单引号双引号都可以 '这个是字符串' "这个也是字符串" #用加号连接字符串 'Hello '+'World' #=>'Hello World' #字符串可以被当做字符列表 'This is a string'[0] #=>'T' #用format来格式化字符串 "{} can be {}".format("string",'interpolated') #=>'string can be interpolated' #可以重复参数以节省时间 "{0} be nimble,{0} be quick,{0} jump over the {1}".format("jack","candle stick") #=>'jack be nimble,jack be quick,jack jump over the candle stick' #如果不想数参数可以用关键词 "{name} wants to eat {food}".format(name='Bob',food='lasagna') #=>'Bob wants to eat lasagna' #如果你的python3程序也要运行在Python2.5以下环境运行,也可以用老式的格式化语法 "%s can be %s the %s way"%('strings','interpolater','old') #=>'strings can be interpolater the old way' (2)None #None是一个对象 None #当与None进行比较时不要用==,要用is,is是用来比较两个变量是否指向同一个对象 'etc' is None #=>False None is None #=>True #None,0,空字符串,空列表,空字典都算是False,其他都是True bool(None) #=>False bool(0) #=>False bool("") #=>False bool({}) #=>False bool([]) #=>False

优秀的个人博客,低调大师

Visual Basic快速入门

Basic语言的全名是Beginner's All-purpose Symbolic Instruction Code(初学者通用的符号指令代码),是几十年前流行过的一门语言。我高中时候一门数学选修课中使用的也是Basic语言。我还记得当时我为了使用Basic语言,还从网上下了一个简陋的QBasic工具。当时第一次接触编程的我对编程语言充满了兴趣。当然在现在,Basic语言已经基本没什么用了。现在还在使用的Basic语言大概就是微软的Visual Basic了。今天就来介绍一下Visual Basic语言。需要注意的是,VB语言运行在微软的CLR上,所以如果你对C#比较熟悉的话,学习VB就很简单了。 HelloWorld 照例,第一个项目自然应该是HelloWorld。打开Visuall Studio,创建一个VB项目,然后就会出现如下图所示的代码。 HelloWorld 来解释一下HelloWorld的代码。首先第一行是导入类库,和C#、java的import语句是一个道理。然后下面定义了一个Program模块,其中定义了Main函数,在函数中输出Hello World。在VB中,不使用大括号作为块分隔符,而是使用End语句来结束块作用域。 Imports System Module Program Sub Main(args As String()) Console.WriteLine("Hello World!") End Sub End Module 基本数据类型 由于VB和C#都是运行在CLR上的语言,所以其实它们的基本类型是一样的,都有有/无符号的各种长度整数、单精度双精度浮点数、布尔类型、字符和字符串等类型。下面的代码仅列出几个常用的。 Public Class DataType Sub DataType() Dim b As Byte Dim i As Integer Dim s As Single Dim d As Double Dim c As Char Dim str As String Dim bool As Boolean b = 100 i = 1000 s = 1.5 d = 3.1415926 c = "c"c str = "abc" bool = True End Sub End Class 变量和常量 VB的变量定义也和C系语言不同,使用的是Dim 变量名 As 变量类型的语法进行定义。变量必须先定义才能使用。如果想同时定义多个变量,可以这么写。 Dim i, j As Integer 如果想在定义变量的同时初始化,可以这样。 Dim bool As Boolean = True 如果要定义常量,可以变量类似,只不过使用Const关键字。 Const cs As Integer = 1000 运算符 先来看看算术运算符。 运算符 用途 例子 ^ 幂运算符 3 ^ 2 = 9 + 加法 - 减法 * 乘法 / 小数除法 3 / 2 = 1.5 \ 整除 3 \ 2 = 1 MOD 取模 6 MOD 4 = 2 然后是比较运算符。这里需要注意的就是VB的相等和不等和其他语言的可能不太一样。 运算符 用途 = 相等 <> 不等 < 小于 > 大于 <= 不大于 >= 不小于 然后是逻辑运算符。 运算符 用途 And 与运算符,不短路求值 Or 或运算符,不短路求值 Not 非运算符 Xor 异或运算符 AndAlso 与运算符,短路求值 OrElse 或运算符,短路求值 IsTrue 是否为真 IsFalse 是否为假 条件语句 If语句 先来看看VB中的If语句,其基本结构是If 条件 Then 执行体 Else 执行体 End If。 Dim num As Integer = 5 If num\2 = 0 Then Console.WriteLine($"{num} is even") Else Console.WriteLine($"{num} is odd") End If 如果需要多重If语句,可以添加ElseIf语句块。 If num = 1 Then Console.WriteLine($"{num} is 1") ElseIf num = 2 Console.WriteLine($"{num} is 2") Else Console.WriteLine($"{num} is other") End If Select语句 Select语句相当于其他语言中的switch语句,如果没有匹配项,会执行Case Else语句块。 Dim c As Char = "c"c Select c Case "a"c Console.WriteLine("a") Case "b"c, "c"c Console.WriteLine("b or c") Case Else Console.WriteLine("others") End Select 循环语句 Do语句循环 Do语句循环有两种结构,第一种是循环条件在前。先来看看Do-While语句,当满足循环条件的时候会继续循环,不满足条件时跳出循环。这里顺便提一下,VB中没有块注释,只有单行注释,以单引号开头,直到句末。 'Do While语句 Dim i = 0 Do While i < 5 If i = 3 Then Exit Do End If Console.Write(i) i += 1 Loop 再来看看Do-Until语句,和Do-While相反,在不满足条件的时候会执行循环,满足条件时跳出循环。 'Do Until语句 i = 0 Do Until i > 5 If i < 3 Then i += 2 Continue Do End If Console.Write(i) i += 1 Loop Do循环的第二种形式就是先执行循环体,然后进行判断,同样有While和Until两种。 i = 0 Do Console.Write(i) i += 1 Loop While i < 5 Console.WriteLine While循环 还有就是传统的While循环。 'While循环 i = 0 While i < 5 Console.Write(i) i += 1 End While For循环 下面是For循环的例子,在For循环中可以使用Step指定步长。 For counter As Integer = 1 To 9 Step 1 Console.Write(counter) Next Foreach循环 Foreach循环用于迭代一个列表中的每一项。 Dim array() as Integer = {1, 2, 3, 4, 5, 6, 7} For Each item As Integer In array Console.Write(item) Next With语句 With语句其实不算循环语句,不过我看的这个VB教程把With语句放到这里说,那我也放到这里好了。With语句在有些语言中也有,主要用途是节省代码数量。比方说有下面这个Person类。 Public Class Person Public Property Name As String Public Property Age As Integer End Class 假如有一个person对象多次出现的话,就可以使用With语句,在With语句中,点访问符默认指向的就是With语句指定的对象。 Dim person As Person = New Person With person .Name = "yitian" .Age = 25 Console.WriteLine($"Person(Name:{.Name}, Age:{.Age})") End With 跳转语句 Exit语句 Exit语句用于结束某个代码块,它的形式如下。想用Exit退出哪个代码块,就写哪个代码块的类型。 Exit { Do | For | Function | Property | Select | Sub | Try | While } Continue语句 Continue语句用于结束当前循环,直接进行下一次循环。它的形式如下,后面跟要继续的代码块类型。 Continue { Do | For | While } Goto语句 最后就是Goto语句,它会直接跳转到指定的标签处。 'Goto语句 GoTo Ending Console.WriteLine("Print something") Ending: Console.WriteLine("This is end.") 数组 先来看看数组定义。VB中的数组比较特殊,定义一个Dim a1(3),其实是下标0-4长度为四的一维数组,这一点要非常注意。 '下标0-9的十个元素的数组 Dim array1(9) As Integer '11X11的二维数组 Dim array2(2, 2) As Integer '定义并初始化数组 Dim array3() = {1, 2, 3, 4, 5} '锯齿数组,也就是数组的数组 Dim array4 As Integer()() = New Integer(1)() {} array4(0) = New Integer() {1, 2} array4(1) = New Integer() {3, 4} 访问数组元素需要使用圆括号,而不是一般语言的方括号。 '初始化一维数组 For i As Integer = 0 To 9 array1(i) = i Next '初始化二维数组 For i = 0 To 2 For j = 0 To 2 array2(i, j) = (i + 1)*(j + 1) Next Next 最后就是遍历数组了,可以使用For循环迭代下标,或者用Foreach循环直接遍历元素。 '显示数组 For Each e In array1 Console.Write(e) Next Console.WriteLine For i As Integer = 0 To 2 For j = 0 To 2 Console.Write(array2(i, j)) Next Console.WriteLine Next For Each e In array3 Console.Write(e) Next Console.WriteLine For i As Integer = 0 To 1 For j = 0 To 1 Console.Write(array4(i)(j)) Next Console.WriteLine Next 函数 Sub函数 回头来看看前面的HelloWorld,其中就有一个Main函数,它是一个Sub函数,也就是没有返回值的函数。 Imports System Module Program Sub Main(args As String()) Console.WriteLine("Hello World!") End Sub End Module 函数可以按值传参,也可以按引用传参,默认情况下是按值传参。 Function GetParam(ByVal a As Integer, ByRef b As Integer) a = 10 b = 10 Console.WriteLine("Param changed") End Function Function函数 Function函数就是有返回值的函数。在函数中,如果要返回值,可以有两种办法,第一种是使用Return语句,第二种是在函数体中向函数名赋值。 '函数返回值可以用Return语句 Function Return1() As Integer Return 1 End Function '也可以向函数名赋值 Function Return2() As Integer Return2 = 2 End Function 可变参数列表 可变参数列表使用ParamArray声明。 Function PrintIntegers(ParamArray integers As Integer()) For Each i In integers Console.Write(i) Next Console.WriteLine End Function 面向对象编程 类 VB的类和C#的类非常相似,同样有字段、属性等概念。构造函数使用New声明,不需要返回值。析构函数使用Finalize声明,也不需要返回值。 Class Contact ' 字段 Private _name As String Private _tel As String '构造函数 Public Sub New(name As String, tel As String) _name = name _tel = tel End Sub '析构函数 Protected Overrides Sub Finalize() End Sub '属性 Public Property Tel As String Get Return _tel End Get Set _tel = value End Set End Property Public Property Name As String Get Return _name End Get Set(value As String) _name = value End Set End Property '重写方法 Public Overrides Function ToString() As String Return $"Contact(Name:{Name}, Tel:{Tel})" End Function End Class 抽象类和接口 如果一个类的方法含有MustOverride修饰符,那么这个方法就是一个抽象方法。含有抽象方法的类就是抽象类,需要使用MustInherit关键字修饰。 MustInherit Class Shape MustOverride Function GetArea() As Double End Class Interface Color Function GetColor() As String End Interface 继承 继承基类和实现接口的声明必须写在类实现的前面。如果一个方法重写了基类的版本,那么这个方法应该使用Overrides关键字修饰。如果不希望类被其他类继承,可以使用NotInheritable修饰,类似于Java的final关键字或者C#的sealed关键字。如果子类需要调用基类的方法,可以使用MyBase关键字代表基类。 Class Circle Inherits Shape Protected radius As Double Public Sub New(r As Double) radius = r End Sub Public Overrides Function GetArea() As Double Return Math.PI*radius*radius End Function End Class NotInheritable Class RedCircle Inherits Circle Implements Color Public Sub New(r As Double) MyBase.New(r) End Sub Public Function Color_GetColor() As String Implements Color.GetColor Return "Red" End Function End Class 静态成员 静态成员和静态函数使用Shared关键字声明,对于每个类来说Shared成员只会存在一个。 Class MyMath Public Shared PI As Double = 3.1415926 Public Shared Function GetPI As Double Return PI End Function End Class 模块 Module类似于Class,不同之处在于Module不需要实例化,其中的函数可以直接调用,就像静态函数一样。 Module Program Sub Main(args As String()) Console.WriteLine("Hello World!") End Sub End Module 异常处理 VB的异常处理和C#的一样,都有Try、Catch、Finally部分。 Public Module ExceptionHandling Sub HandleException() Try Throw New ArithmeticException("除数不能为零") Catch ex As ArithmeticException Console.WriteLine($"捕获了数学计算异常:{ex.Message}") Catch ex As Exception Console.WriteLine($"捕获了异常:{ex.Message}") Finally Console.WriteLine("终结代码") End Try End Sub End Module 以上就是VB语言的一些介绍了,希望对大家能有所帮助。

优秀的个人博客,低调大师

[Phoenix] 一、快速入门

Phoenix作为应用层和HBASE之间的中间件,以下特性使它在大数据量的简单查询场景有着独有的优势 二级索引支持(global index + local index) 编译SQL成为原生HBASE的可并行执行的scan 在数据层完成计算,server端的coprocessor执行聚合 下推where过滤条件到server端的scan filter上 利用统计信息优化、选择查询计划(5.x版本将支持CBO) skip scan功能提高扫描速度 一般可以使用以下三种方式访问Phoenix JDBC API 使用Python编写的命令行工具(sqlline, sqlline-thin和psql等) SQuirrel 一、命令行工具psql使用示例 1.创建一个建表的sql脚本文件us_population.sql: CREATE TABLE IF NOT E

优秀的个人博客,低调大师

Docker镜像管理快速入门

简介 介绍如何使用Docker构建镜像,并通过阿里云镜像服务分发到ECS服务器,运行该镜像。 背景知识 容器技术容器是一个允许我们在资源隔离的过程中,运行应用程序和其依赖项的 、轻量的 、操作系统级别的虚拟化技术, 运行应用程序所需的所有必要组件都打包为单个镜像,这个镜像是可以重复使用的。当镜像运行时,它是运行在独立的环境中,并不会和其他的应用共享主机操作系统的内存、CPU或磁盘。这保证了容器内的进程不会影响到容器外的任何进程。 镜像仓库(Registry)Docker的镜像存储中心通常被称为Registry。当您需要获取自己私有镜像时,首先需要登录Registry,然后拉取镜像。修改过镜像之后,您可以再次将镜像推送回Registry中去。或者在本地通过Docker镜像构建的功能生成镜像,再推送到Registry中。 容器镜像服务ACR(Alibaba Cloud Container Registry)阿里云容器镜像服务ACR默认实例版提供基础的容器镜像服务,包括安全的应用镜像托管能力、精确的镜像安全扫描功能、稳定的国内外镜像构建服务以及便捷的镜像授权功能,从而方便用户进行镜像全生命周期管理。 连接ECS服务器(如未购买可在此处体验ECS) 1.打开系统自带的终端工具。Windows:CMD或Powershell。MAC:Terminal。 在终端中输入连接命令ssh [username]@[ipaddress]。您需要将其中的username和ipaddress替换为第1小节中创建的ECS服务器的登录名和公网地址。例如: ssh root@123.123.123.123 命令显示结果如下: 3.输入yes,同意继续后将会提示输入登录密码。 密码为已创建的云服务的ECS的登录密码。 搭建Docker服务 Docker 是一个开源的容器引擎,用于创建、管理和编排容器,可以轻松为任何应用创建一个轻量级、可移植、自给自足的容器。本步骤将在ECS上部署一个Docker服务,并配置DockerHub的镜像加速器。 安装Docker的依赖库。 yum install -y yum-utils device-mapper-persistent-data lvm2 添加Docker CE的软件源信息。 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 安装Docker CE。 yum makecache fast && yum -y install docker-ce 启动Docker服务。 systemctl start docker 配置DockerHub镜像加速器。 tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://registry.docker-cn.com"] } EOF 重启Docker服务。 systemctl restart docker 准备应用代码和Dockerfile 本步骤操作将在工作空间下创建一个基于Golang的HelloWorld代码文件和一个用来构建运行Hello代码所需环境镜像的Dockerfile文件。 创建工作空间。 mkdir -p /tmp/demo && cd /tmp/demo 在工作空间下创建HelloWorld代码文件,用来在容器环境中监听HTTP服务,输出HelloWorld字符串。 cat > /tmp/demo/main.go << EOF package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello! World\n") }) fmt.Println("start to serve...") http.ListenAndServe(":80", nil) } EOF 在工作空间下创建Dockerfile文件。 cat > /tmp/demo/Dockerfile << EOF FROM golang:1.12-alpine # change current working dir WORKDIR /go/src/app # copy main.go into /go/src/app COPY . . # go build and install the app RUN go install -v ./... # run the app by default CMD ["app"] EOF 本地构建镜像并运行镜像 本步骤将指导您使用Docker基本命令构建和运行镜 使用docker build命令构建镜像。 docker build . -t demo:v1 命令运行结果如下所示: 说明:参数.表示指定当前路径作为构建上下文,即Dockerfile所在的本地路径。参数-t demo:v1指定镜像名称和标签。 使用docker run命令运行镜像。 docker run -d -p 8000:80 demo:v1 命令运行结果如下所示: 说明: 参数-d设置容器运行模式为后台运行。参数-p 8000:80将容器内部使用的网络端口映射到主机上,其中8000为主机端口,80为容器内部使用端口。 使用curl工具访问容器中的HelloWorld服务。 curl localhost:8000 命令运行结果如下所示: 4.使用docker rm命令删除容器。 docker rm -f $(docker ps -a | grep "demo:v1" | awk '{print $1}') 命令运行结果如下所示: 创建远程镜像仓库 本步骤操作将指导您使用自己的阿里云账号开通容器镜像服务并创建镜像仓库。阿里云容器镜像服务默认实例版可免费使用,其使用限制请参见容器镜像服务计费说明。 1.使用您的阿里云主账号登录容器镜像服务控制台。 2.【前往开通】》单击【设置Registry登录密码】》设置阿里云Docker Registry登录密码,然后单击 【确定】 。3.在容器镜像服务控制台,依次单击 【默认实例】 > 【命名空间】 > 【创建命名空间】,在 【创建命名空间】 弹框中填写命令空间名称,然后单击 【确定】 。 单击 【本地仓库】 > 【创建镜像仓库】 。在镜像仓库列表,选择上一步创建的镜像仓库,单击 【管理】 ,查看镜像仓库详情。 推送镜像 本步骤操作将本地镜像推送到远程仓库中,并运行远程仓库中的指定版本镜像。请将本步骤命令中涉及的远程镜像地址替换为步骤六中创建的镜像仓库的公网地址。 1.执行以下命令登录到阿里云Docker Registry。 docker login --username="用户名" registry.cn-hangzhou.aliyuncs.com 说明: 请将下面命令中的用户名替换为您的阿里云账号全名,回车后输入远程镜像仓库密码,密码为步骤六开通服务时设置的密码。 请登录阿里云用户中心查看您的主账号用户名称。命令运行结果如下所示: 2.标记本地镜像,将其归入远程仓库。 docker tag demo:v1 registry.cn-hangzhou.aliyuncs.com/space_test/demo:v1 3.将本地镜像推送到远程仓库。 docker push registry.cn-hangzhou.aliyuncs.com/space_test/demo:v1 命令运行结果如下所示: 拉取指定版本的远程镜像。 docker pull registry.cn-hangzhou.aliyuncs.com/space_test/demo:v1 命令运行结果如下所示: 运行拉取的远程镜像。 docker run -d -p 8000:80 registry.cn-hangzhou.aliyuncs.com/space_test/demo:v1 命令运行结果如下所示: 访问HelloWorld服务。 curl localhost:8000 命令运行结果如下所示:

优秀的个人博客,低调大师

快速入门 WePY 小程序

一、WePY介绍 WePY 是腾讯参考了Vue 等框架对原生小程序进行再次封装的框架,更贴近于 MVVM 架构模式, 并支持ES6/7的一些新特性。 二、WePY 使用 1、WePY的安装或更新都通过npm进行: npm install -g wepy-cli //全局安装或更新WePY命令行工具(wepy脚手架): wepy-cli wepy -v //查看wepy-cli版本 wepy init standard <projectName> //新建wepy小程序项目,1.7.0之前的版本使用:wepy new myproject wepy list //查看项目模板 cd <projectName> //切换至项目目录 npm install //安装依赖 wepy build --watch //开启实时编译 git base here: (1)全局安装或更新 wepy-cli: (2)查看 wepy-cli 版本: (3)创建 wepy 小程序项目: (4)切换至项目目录,安装依赖: (5)开启实时编译 2、代码高亮WebStorm/PhpStorm(其他工具参见:wepy官网代码高亮) (1)打开Settings,搜索Plugins,搜索Vue.js插件并安装。 (2) 打开Settings,搜索File Types,找到Vue.js Template,在Registered Patterns添加*.wpy,即可高亮。 3、代码优化 (1)代码规范 wepy标签和原生一样 自定义组件命名应避开:原生组件名(input、button、view、repeat等)、WePY的辅助标签<repeat> 变量/方法名尽量使用驼峰式命名,避免使用$开头($开头的标识符是WePY内建属性/方法,可在js中以this.的方式直接使用,具体请参考API文档) app、pages、components文件名的后缀为.wpy,外链的文件可以是其它后缀。 具体请参考wpy文件说明。 支持ES6/7的一些新特性,框架在ES6(ECMAScript 6)下开发(默认使用babel编译),因此也需要使用ES6开发小程序,ES6中有大量的语法糖可以让我们的代码更加简洁高效。 wepy继承了wx对象的方法,建议在wepy框架开发中不要用到wx对象的方法,虽然运行时效果是一样,但是打包时会cli报错(wepy中没有wx对象)wepy中组件中使用的是class,vue中使用的的是对象。 (2)数据绑定 小程序页面渲染层和JS逻辑层分开的,setData操作实际就是JS逻辑层与页面渲染层之间的通信,在同一次运行周期内多次执行setData操作时,通信的次数是一次还是多次取决于API本身的设计。WePY使用脏数据检查对setData进行封装,在函数运行周期结束时执行脏数据检查,一来可以不用关心页面多次setData是否会有性能上的问题,二来可以更加简洁去修改数据实现绑定,不用重复去写setData方法。 //原生小程序 this.setData({title: 'this is title'});//通过Page提供的setData方法来绑定数据 //wepy this.title = 'this is title'; //wepy 在异步函数中更新数据的时候,必须手动调用$apply方法,才会触发脏数据检查流程的运行 setTimeout(() => { this.title = 'this is title'; this.$apply(); }, 3000) //保留setData方法,但不建议使用setData执行绑定,修复传入undefined的bug,并且修改入参支持: this.setData(target, value) this.setData(object) (3)事件绑定以及传参优化 <view @tap="click"></view> //​​​​​ 原 bindtap="click"(省略了.default后缀 )绑定小程序冒泡型事件 <view @tap.stop="click"></view> //​​​​​ 原catchtap="click" 绑定小程序捕获型事件,如catchtap <view @tap.capture="click"></view> //​​​​​ 原 capture-bind:tap="click" <view @tap.capture.stop="click"></view> //​​​​​ 原 capture-catch:tap="click" <!--事件传参--> <view @tap="click({{index}})"></view> //​​​​​ 原bindtap="click" data-index={{index}} (4)框架默认对小程序提供的API全都进行了 Promise 处理,甚至可以直接使用async/await等新特性进行开发,同时修复了一些原生API的缺陷(如:wx.request的并发问题等) // 原生代码: wx.request({ url: 'xxx', success: function (data) { console.log(data); } }); // WePY 使用方式, 需要开启 Promise 支持,参考开发规范章节 wepy.request('xxxx').then((d) => console.log(d)); // async/await 的使用方式, 需要开启 Promise 和 async/await 支持,参考 WIKI async function request () { let d = await wepy.request('xxxxx'); console.log(d); } (5)computed 计算属性computed计算属性(类型:{ [key: string]: Function }),是一个有返回值的函数,可直接被当作绑定数据来使用,类似于data属性。需要注意的是,只要是组件中有任何数据发生了改变,那么所有计算属性就都会被重新计算。 data = { a: 1 } // 计算属性aPlus,在脚本中可通过this.aPlus来引用,在模板中可通过{{ aPlus }}来插值 computed = { aPlus () { return this.a + 1 } } (6)watcher 监听器 通过监听器watcher(类型:{ [key: string]: Function })能够监听到任何属性的更新。监听器适用于当属性改变时需要进行某些额外处理的情形。 data = { num: 1 } // 监听器函数名必须跟需要被监听的data对象中的属性num同名, // 其参数中的newValue为属性改变后的新值,oldValue为改变前的旧值 watch = { num (newValue, oldValue) { console.log(`num value: ${oldValue} -> ${newValue}`) } } // 每当被监听的属性num改变一次,对应的同名监听器函数num()就被自动调用执行一次 onLoad () { setInterval(() => { this.num++; this.$apply(); }, 1000) } (7)WXS (WeiXin Script) WePY 从1.7.x版本开始支持 wxs 语法,但语法与原生 wxs 稍有出入 a.wxs是基于原生的wxs去实现的,只是通过编译把现在的语法编译为原生语法 ②wxs必须是外链文件。并且后缀为.wxs ③wxs引入后只能在template中使用,不能在script中使用 /** project └── src ├── wxs | └── mywxs.wxs wxs 文件 ├── pages | └── index.wpy 页面 └──app.wpy **/ // mywxs.wxs module.exports = { text: 'This is from wxs', filter: function (num) { return num.toFixed(2); } }; // index.wpy <template> <text>{{m1.text}}</text> <text>{{m1.filter(num)}}</text> </template> <script> import wepy from 'wepy'; import mywxs from '../wxs/mywxs.wxs'; export default class Index extends wepy.page { data = { num: 10 }; wxs = { m1: mywxs } }; </script> (8)interceptor 拦截器 可以使用WePY提供的全局拦截器对原生API的请求进行拦截。具体方法是配置API的config、fail、success、complete回调函数。参考示例: import wepy from 'wepy'; export default class extends wepy.app { constructor () { // this is not allowed before super() super(); // 拦截request请求 this.intercept('request', { // 发出请求时的回调函数 config (p) { // 对所有request请求中的OBJECT参数对象统一附加时间戳属性 p.timestamp = +new Date(); console.log('config request: ', p); // 必须返回OBJECT参数对象,否则无法发送请求到服务端 return p; }, // 请求成功后的回调函数 success (p) { // 可以在这里对收到的响应数据对象进行加工处理 console.log('request success: ', p); // 必须返回响应数据对象,否则后续无法对响应数据进行处理 return p; }, //请求失败后的回调函数 fail (p) { console.log('request fail: ', p); // 必须返回响应数据对象,否则后续无法对响应数据进行处理 return p; }, // 请求完成时的回调函数(请求成功或失败都会被执行) complete (p) { console.log('request complete: ', p); } }); } } 三、WePY项目的目录结构 dist目录为WePY通过build指令生成的目录,除额外增加的npm目录外,其目录结构与原生小程序的目录结构类似。 原生小程序:app(app.js、app.json、app.wxss),page(page.js、page.json、page.wxml、page.wxss),文件必须同名。WePY中则使用了单文件模式:app.wpy,page.wpy。 ==> 一个.wpy文件可分为三大部分,各自对应于一个标签: 脚本<script>部分,又可分为两个部分: 逻辑部分,除了config对象之外的部分,对应于原生的.js文件 配置部分,即config对象,对应于原生的.json文件 结构<template>部分,对应于原生的.wxml文件 样式<style>部分,对应于原生的.wxss文件 <!--lang决定了其代码编译过程,src决定是否外联代码,存在src属性且有效时,会忽略内联代码--> <style lang="less" src="page1.less"></style>//lang值:css(默认)、less、scss、stylus、postcss <template lang="wxml" src="page1.wxml"></template>//lang值:wxml(默认)、xml、pug(原jade)。入口文件app.wpy不需要template,所以编译时会被忽略。 <script></script>//lang值:babel(默认、TypeScript 主要对目录的以下几点分析(其他目录文件详解类似于https://my.oschina.net/wangnian/blog/2050375中做的分析): 1、src文件夹 (1)components组件: 组件实例继承自wepy.component类,除了不需要config配置以及页面特有的一些生命周期函数之外,其属性与页面属性大致相同: import wepy from 'wepy'; export default class MyComponent extends wepy.component { props = {}//接收父组件传来的参数 customData = {} // 自定义数据 customFunction () {} //自定义方法 onLoad () {} // 在Page和Component共用的生命周期函数 data = {}; // 页面所需数据均需在这里声明,可用于模板数据绑定 components = {}; // 声明页面中所引用的组件,或声明组件中所引用的子组件 mixins = []; // 声明页面所引用的Mixin实例 computed = {}; // 声明计算属性(详见后文介绍) watch = {}; // 声明数据watcher(详见后文介绍) methods = {}; // 声明页面wxml中标签的事件处理函数。注意,此处只用于声明页面wxml中标签的bind、catch事件,自定义方法需以自定义方法的方式声明,不能放在methods中,会报错 events = {}; // WePY组件事件处理函数对象,存放响应组件之间通过$broadcast、$emit、$invoke所传递的事件的函数 } /** 与page不同的是component不存在: onShow () {} // 只在Page中存在的页面生命周期函数 config = {}; // 只在Page实例中存在的配置数据,对应于原生的page.json文件,类似于app.wpy中的config onReady() {} // 只在Page中存在的页面生命周期函数 **/ 原生小程序支持js模块化,但彼此独立,业务代码与交互事件仍需在页面处理。无法实现组件化的松耦合与复用(如,模板A中绑定一个bindtap="myclick",模板B中同样绑定一样bindtap="myclick",那么就会影响同一个页面事件、数据) WePY组件的所有业务与功能在组件本身实现,组件与组件之间彼此隔离(上述例子在WePY的组件化开发过程中,A组件只会影响到A所绑定的myclick,B也如此) // 原生代码: <!-- item.wxml --> <template name="item"> <text>{{text}}</text> </template> <!-- index.wxml --> <import src="item.wxml"/> <template is="item" data="{{text: 'forbar'}}"/> <!-- index.js --> var item = require('item.js') // WePY <!-- /components/item.wpy --> <text>{{text}}</text> <!-- index.wpy --> <template> <com></com> </template> <script> import wepy from 'wepy'; import Item from '../components/item'; export default class Index extends wepy.page { components = { com: Item } } </script> ps,在 1.7.2-alpha4 的实验版本中提供了对原生组件的支持 a.引用组件 页面可以引入组件,而组件还可以引入子组件 /** project └── src ├── components | └── child.wpy ├── pages | ├── index.wpy index 页面配置、结构、样式、逻辑 | └── log.wpy log 页面配置、结构、样式、逻辑 └──app.wpy 小程序配置项(全局公共配置、公共样式、声明钩子等) **/ // index.wpy <template> <!-- 以`<script>`脚本部分中所声明的组件ID为名命名自定义标签,从而在`<template>`模板部分中插入组件 --> <view class="child1"> <child></child> </view> <view class="child2"> <!--注意:WePY中,在父组件template模板部分插入驼峰式命名的子组件标签时,不能将驼峰式命名转换成短横杆式命名(比如将childCom转换成child-com,这与Vue中不一致)--> <anotherchild></anotherchild> </view> </template> <script> import wepy from 'wepy'; //引入组件文件 import Child from '../components/child'; export default class Index extends wepy.component { //声明组件,分配组件id为child,需要注意的是,WePY中的组件都是静态组件,是以组件ID作为唯一标识的,每一个ID都对应一个组件实例,当页面引入两个相同ID的组件时,这两个组件共用同一个实例与数据,当其中一个组件数据变化时,另外一个也会一起变化。 components = { //为两个相同组件的不同实例分配不同的组件ID,从而避免数据同步变化的问题 child: Child, anotherchild: Child }; } </script> ②循环渲染组件(1.4.6新增) WePY 1.x 版本中,循环渲染WePY组件时(类似于通过wx:for循环渲染原生的wxml标签),必须使用WePY定义的辅助标签<repeat> WePY组件是静态编译组件(在编译阶段编译进页面),每个组件都是唯一的一个实例,目前只提供简单的repeat支持(不支持在repeat的组件中去使用props,computed,watch等),因此如下: <!-- 错误使用 ---> // list.wpy <view>{{test.name}}</view> // index.wpy <repeat for="{{mylist}}"> <List :test.sync="item"></List> </repeat> <!-- 推荐用法 ---> // list.wpy <repeat for="{{mylist}}"> <view>{{item.name}}</view> </repeat> // index.wpy <List :mylist.sync="mylist"></List> ③props 传值 props传值在WePY中属于父子组件之间传值的一种机制,包括静态传值与动态传值。 静态传值为父组件向子组件传递常量数据,因此只能传递String字符串类型。 动态传值是指父组件向子组件传递动态数据内容,父子组件数据完全独立互不干扰。但可以通过使用.sync修饰符来达到父组件数据绑定至子组件的效果,也可以通过设置子组件props的twoWay: true来达到子组件数据绑定至父组件的效果。如果既使用.sync修饰符,同时子组件props中添加的twoWay: true时,就可以实现数据的双向绑定。 // parent.wpy <!--在父组件template模板部分所插入的子组件标签中,使用:prop属性(等价于Vue中的v-bind:prop属性)来进行动态传值--> <child :title="parentTitle" :syncTitle.sync="parentTitle" :twoWayTitle="parentTitle"></child> <!--通过使用.sync修饰符来达到父组件数据绑定至子组件的效果--> <script> data = { parentTitle: 'p-title' }; </script> // child.wpy <script> props = { // 静态传值 title: String, // 父向子单向动态传值 syncTitle: { type: String, default: 'null' }, // 双向绑定 twoWayTitle: { type: String, default: 'nothing', twoWay: true//twoWay: true(默认false)来达到子组件数据绑定至父组件的效果,如果同时使用.sync修饰符,就可以实现双向绑定 } }; onLoad () { console.log(this.title); // p-title console.log(this.syncTitle); // p-title console.log(this.twoWayTitle); // p-title this.title = 'c-title'; console.log(this.$parent.parentTitle); // p-title. this.twoWayTitle = 'two-way-title'; this.$apply(); console.log(this.$parent.parentTitle); // two-way-title. --- twoWay为true时,子组件props中的属性值改变时,会同时改变父组件对应的值 this.$parent.parentTitle = 'p-title-changed'; this.$parent.$apply(); console.log(this.title); // 'c-title'; console.log(this.syncTitle); // 'p-title-changed' --- 有.sync修饰符的props属性值,当在父组件中改变时,会同时改变子组件对应的值。 } </script> ④组件通信与交互 wepy.component基类提供$broadcast、$emit、$invoke三个方法用于组件之间的通信和交互: $broadcast $broadcast事件是由父组件发起,所有子组件都会收到此广播事件,除非事件被手动取消。事件广播的顺序为广度优先搜索顺序 $emit $emit与$broadcast正好相反,组件发起事件后所有祖先组件会依次接收到$emit事件 //子组件: this.$emit('some-event', 1, 2, 3, 4); //父组件: import wepy from 'wepy' export default class Com extends wepy.component { components = {}; data = {}; methods = {}; // events对象中所声明的函数为用于监听组件之间的通信与交互事件的事件处理函数 events = { 'some-event': (p1, p2, p3, $event) => { console.log(`${this.$name} receive ${$event.name} from ${$event.source.$name}`); } }; // Other properties } $invoke $invoke是一个页面/组件对另一个组件中的方法的直接调用,通过传入组件路径找到相应的组件,然后再调用其方法。 this.$invoke('ComA', 'someMethod', 'someArgs');//在页面Page_Index中调用组件ComA的某个方法 this.$invoke('./../ComB/ComG', 'someMethod', 'someArgs');//在组件ComA中调用组件ComG的某个方法 //只能在methods中使用 在周期函数使用会报错 ⑤组件自定义事件处理函数(.user事件后缀,1.4.8新增) 示例如下(注意,如果用了自定义事件,则events中对应的监听函数不会再执行): // index.wpy <template> <child @childFn.user="parentFn"></child> </template> <script> import wepy from 'wepy' import Child from '../components/child' export default class Index extends wepy.page { components = { child: Child } methods = { parentFn (num, evt) { console.log('parent received emit event, number is: ' + num) } } } </script> // child.wpy <template> <view @tap="tap">Click me</view> </template> <script> import wepy from 'wepy' export default class Child extends wepy.component { methods = { tap () { console.log('child is clicked') this.$emit('childFn', 100) } } } </script> ⑥slot 组件内容分发插槽 WePY中的slot插槽作为内容分发标签的空间占位标签,便于在父组件中通过对相当于扩展板卡的内容分发标签的“插拔”,更为灵活、方便地对子组件进行内容分发。 注意,父组件中的标签必须有slot属性,且值为子组件中对应的slot名,这样父组件内容分发标签中的内容(即便没有内容,子组件插槽中的默认内容也不会显示出来,只有删除了父组件中对应的内容分发标签,才能显示出来)会覆盖掉子组件对应插槽中的默认内容 //首先在子组件template模板部分中声明slot标签作为内容插槽,同时必须在其name属性中指定插槽名称,还可设置默认的标签内容 <view class="panel"> <slot name="title">默认标题</slot> <slot name="content">默认内容</slot> </view> //然后在引入了该带有插槽的子组件的父组件template模板部分中声明用于“插拔”的内容分发标签 <panel> <view slot="title">新的标题</view> <view slot="content"> <text>新的内容</text> </view> </panel> eg:message-com.wpy: <template> <!-- 1.4.6新增:循环渲染时(类似于原生的wx:for),必须使用WePY定义的辅助标签"<repeat>" --> <repeat for="{{messageList}}" index="index" item="message"> <navigator url="/pages/topicDetail?topicId={{message.busiId}}"> <view class="msgList"> <!-- "<text selectable='true'>" 使文字可选--> <text selectable='true'>{{message.content}}</text> <view style="text-align: right;"> <text style="color: rgb(202,202,202);">{{message.createTime}}</text> </view> </view> </navigator> </repeat> </template> <script> import wepy from 'wepy' //引入wepy export default class Message extends wepy.component {//创建组件实例 props = {//接收父组件参数 messageList: {// * 类型和默认值参数不能省略,组件会偶发性接收不到父组件传递的参数 type: Array, default:[] } }; } </script> <style scoped lang="less"> .topicInfo text { font-size: 12px; color: #666; } .topicBottom view { margin-right: 15px; } .msgList { background-color: #fff; margin-top: 7px; padding: 10px 15px; } </style> message-page.wpy: <template> <view wx:if="{{!messageList.length}}" class="noData">暂无消息</view> <block wx:else> <message :messageList.sync="messageList"></message> <downLoad :show.sync="getListParam"></downLoad> </block> </template> <script> import wepy from 'wepy' import Message from '../components/message' import DownLoad from '../components/downLoad' import listMixin from '../mixins/list' import {connect} from 'wepy-redux' import {getMessageList} from '../store/actions' @connect({ messageList(state) { return state.reducer.messageList; } }, { getMessageList }) export default class MineMessages extends wepy.page { config = { navigationBarTitleText: '我的消息' }; components = { message: Message, downLoad: DownLoad }; data = {}; mixins = [listMixin]; methods = { getList(params) { let listParam = { messageType: 4 }; if (wx.getStorageSync('userId')) { this.getMessageList(Object.assign(listParam, params)); } else { wx.showToast({ title: '您还没有登录', icon: 'none' }); } } }; } </script> (2)mixins:Mixin 混合(公用的js) 混合可以将组件之间的可复用部分抽离,从而在组件中使用混合时,可以将混合的数据,事件以及方法注入到组件之中。混合分为两种: 默认式混合(data、components、events、自定义方法),即组件未声明的选项将混合对象中注入组件之中,组件已声明的选项将不受影响 兼容式混合(methods响应事件 、小程序页面事件),即先响应组件本身响应事件,然后再响应混合对象中响应事件(Vue则相反,先执行mixin中的函数, 再执行组件本身的函数) eg:listMixin.js: import wepy from 'wepy' export default class ListMixin extends wepy.mixin {//创建mixin实例 config = { enablePullDownRefresh: true,//开启下拉刷新,默认是关闭的 onReachBottomDistance: 10,//设置触发下拉刷新的底部距离 backgroundColor: "#eab010"//设置背景色 }; data = { getListParam: {//获取列表数据的通用参数 hasMoreData: true, loading: false, noMore: false, page: 1, limit: 10, } }; /** * 页面相关事件处理函数--监听用户下拉动作(下拉刷新) */ onPullDownRefresh() { wx.showNavigationBarLoading(); //在标题栏中显示加载 this.data.getListParam.page = 1;//下拉刷新时参数page=1 this.methods.getList(this.data.getListParam);//调用组件中的获取列表函数 setTimeout(() => { wx.hideNavigationBarLoading(); //完成停止加载 wx.stopPullDownRefresh(); //停止下拉刷新 }, 300) }; /** * 页面上拉触底事件的处理函数(上拉加载) */ onReachBottom() { if (this.data.getListParam.hasMoreData) { this.data.getListParam.page++;//每触发一次page++ this.data.getListParam.hasMoreData = false;//关闭可调用函数 this.data.getListParam.loading = true;//显示加载中...(自己写的底部加载中样式的组件) this.methods.getList(this.data.getListParam);//调用组件中的获取列表函数 setTimeout(() => { this.data.getListParam.loading = false;//关闭显示加载中...(自己写的底部加载中样式的组件) this.$apply();//强制渲染 }, 100); } else { this.data.getListParam.noMore = true;//显示加载完成(自己写的底部加载到底样式的组件) } }; onReady() { this.methods.getList(this.data.getListParam);//初始化请求页面数据 } } 使用时引入然后注入到组件实例(最好放在data属性后,否则会偶发性报错),eg: (3)页面page.wpy Page页面实际上继承自Component组件,即Page也是组件。除扩展了页面所特有的config配置以及特有的页面生命周期函数之外,其它属性和方法与Component一致: import wepy from 'wepy'; export default class MyPage extends wepy.page { customData = {} // 自定义数据 customFunction () {} //自定义方法 onLoad () {} // 在Page和Component共用的生命周期函数 onUnload() {} // 监听页面卸载 onReady() {} // 只在Page中存在的页面生命周期函数 onShow () {} // 只在Page中存在的页面生命周期函数,当小程序启动,或从后台进入前台显示 onHide() {} // 只在Page中存在的页面生命周期函数,当小程序从前台进入后台 config = {}; // 只在Page实例中存在的配置数据,对应于原生的page.json文件,类似于app.wpy中的config data = {}; // 页面所需数据均需在这里声明,可用于模板数据绑定 components = {}; // 声明页面中所引用的组件,或声明组件中所引用的子组件 mixins = []; // 声明页面所引用的Mixin实例 computed = {}; // 声明计算属性(详见后文介绍) watch = {}; // 声明数据watcher(详见后文介绍) methods = {}; // 声明页面wxml中标签的事件处理函数。注意,此处只用于声明页面wxml中标签的bind、catch事件,自定义方法需以自定义方法的方式声明 events = {}; // WePY组件事件处理函数对象,存放响应组件之间通过$broadcast、$emit、$invoke所传递的事件的函数 onPullDownRefresh(){} // 监听用户下拉动作 onReachBottom(){} // 页面上拉触底事件的处理函数 onShareAppMessage(){} // 用户点击右上角分享 onPageScroll(){} // 页面滚动 onTabItemTap(){} // 当前是 tab 页时,点击 tab 时触发 } (4)状态管理store(redux) (5)app.wpy:小程序入口 app.wpy实例继承自wepy.app类,包含:config配置对象(对应原生的app.json文件,build编译时会根据config属性自动生成app.json文件)、globalData全局数据对象、自定义方法与属性、小程序生命周期函数。 在Page页面实例中,可以通过this.$parent来访问App实例。 <script> import wepy from 'wepy'; export default class extends wepy.app { config = { pages:[// pages定义当前小程序所有页面路径,让微信客户端知道当前你的小程序页面定义在哪个目录 'pages/index', 'pages/mine' ], window:{//window定义小程序所有页面的顶部背景颜色,文字颜色定义等。 "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "顶部标题", "navigationBarTextStyle": "black" }, tabBar: { selectedColor: '#ea9100',//底部导航字体选中时的颜色 list: [ { pagePath: 'pages/index',//导航页面路径(必须在pages中定义) text: '首页',//导航名 iconPath: 'img/index.png',//未选中的导航icon selectedIconPath: 'img/index1.png'//选中时的导航icon }, { pagePath: 'pages/mine', text: '我的', iconPath: 'img/mine.png', selectedIconPath: 'img/mine1.png' } ] }, }; onLaunch() {//初始化 console.log(this); } onShow(){}//当小程序启动,或从后台进入前台显示 onHide(){}//当小程序从前台进入后台 } </script> <style lang="less"> /** less **/ </style> (6)index.template.html:web页面的入口文件 2、project.config.json小程序项目配置文件 1.7.0 之后的版本会在根目录包含project.config.json,使用微信开发者工具-->添加项目,项目目录请选择项目根目录即可根据配置完成项目信息自动配置。 1.7.0 之前生成的代码包可能不存在project.config.json文件,建议手动创建该文件后再添加项目。project.config.json文件内容如下: { "description": "project description", "setting": { "urlCheck": true,//对应不检查安全域名选项,开启。 如果已配置好安全域名则建议关闭 "es6": false,//对应关闭ES6转ES5选项,关闭。 重要:未关闭会运行报错 "postcss": false,//对应关闭上传代码时样式自动补全选项,关闭。 重要:某些情况下漏掉此项也会运行报错 "minified": false //对应关闭代码压缩上传选项,关闭。重要:开启后,会导致真机computed, props.sync 等等属性失效。(注:压缩功能可使用WePY提供的build指令代替) }, "compileType": "miniprogram", "appid": "touristappid", "projectname": "Project name", "miniprogramRoot": "./dist" } 3、wepy.config.js const path = require('path'); var prod = process.env.NODE_ENV === 'production'; module.exports = { /*wpyExt:缺省值为'.wpy',IDE默认情况下不会对此文件类型进行高亮处理 除了进行前文代码高亮设置之外 还可以直接将相关文件的后缀名由.wpy修改为.vue,然后将此选项修改为.vue */ wpyExt: '.wpy', eslint: false, cliLogs: !prod, build: { web: { htmlTemplate: path.join('src', 'index.template.html'), htmlOutput: path.join('web', 'index.html'), jsOutput: path.join('web', 'index.js') } }, resolve: { alias: { counter: path.join(__dirname, 'src/components/counter'), '@': path.join(__dirname, 'src') }, aliasFields: ['wepy', 'weapp'], modules: ['node_modules'] }, /** compilers为1.3.1版本之后的功能,如果需要使用其它语法,请先配置compilers,然后再安装相应的compilers 目前支持wepy-compiler-less, wepy-compiler-postcss,wepy-compiler-sass、wepy-compiler-babel、wepy-compiler-pug 其他compiler持续开发中 */ compilers: { less: { compress: prod }, /*sass: { outputStyle: 'compressed' },*/ babel: { sourceMap: true, presets: [ 'env' ], /* plugins为1.1.6版本之后的功能 目前支持js压缩wepy-plugin-ugliyjs、图片压缩wepy-plugin-imagemin 其他plugin持续开发中 */ plugins: [ 'transform-class-properties', 'transform-decorators-legacy', 'transform-object-rest-spread', 'transform-export-extensions', ] } }, plugins: {}, /*在使用的地方先引入import wepy from 'wepy'; 然后wepy.$appConfig.属性名。eg:wepy.$appConfig.baseUrl*/ appConfig: { baseUrl:process.env.NODE_ENV === 'production'? 'https://api.a.com/' : 'https://api.a.com/dev/',// 运行 npm run dev命令时,域名为https://api.a.com/dev/,运行 npm run build时域名为https://api.a.com/ noPromiseAPI: ['createSelectorQuery'] } } if (prod) { // 压缩sass module.exports.compilers['less'] = {outputStyle: 'compressed'} // 压缩js module.exports.plugins = { uglifyjs: { filter: /\.js$/, config: {} }, imagemin: { filter: /\.(jpg|png|jpeg)$/, config: { jpg: { quality: 80 }, png: { quality: 80 } } } } } 相关链接:mpvue文档、原生小程序文档、公众号文档 博客地址:https://my.oschina.net/wangnian

优秀的个人博客,低调大师

Vuex 快速入门 简单易懂

一、vuex介绍 (1)vuex是什么? 1. 借鉴了Flux、Redux、The Elm Architecture 2. 专为 Vue.js 设计的状态管理模式 3. 集中式存储和管理应用程序中所有组件的状态 4. Vuex 也集成到 Vue 的官方调试工具 5. 一个 Vuex 应用的核心是 store(仓库,一个容器),store包含着你的应用中大部分的状态 (state)。 (2)什么情况下我应该使用 Vuex? 1. 不适用:小型简单应用,用 Vuex 是繁琐冗余的,更适合使用简单的store 模式。 2. 适用于:中大型单页应用,你可能会考虑如何把组件的共享状态抽取出来,以一个全局单例模式管理,不管在哪个组件,都能获取状态/触发行为,解决问题如下: ① 多个视图使用于同一状态: 传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力 ② 不同视图需要变更同一状态: 采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝,通常会导致无法维护的代码 (3)Vuex 和单纯的全局对象有何不同? 1.Vuex 的状态存储是响应式的 当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。 2.你不能直接改变 store 中的状态 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation,方便我们跟踪每一个状态的变化。 二、vuex安装 * vue cli 3 中搭建脚手架预设时选择了“vuex”后便安装了vuex,可跳过此步阅读 (1)<script>引入 在 Vue 之后引入vuex会进行自动安装: <script src="/path/to/vue.js"></script> <script src="/path/to/vuex.js"></script> (2) 包管理 npm install vuex --save //yarn add vuex 在一个模块化的打包系统中,您必须显式地通过 Vue.use() 来安装 Vuex: import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) (3)git clone 自己构建 git clone https://github.com/vuejs/vuex.git node_modules/vuex cd node_modules/vuex npm install npm run build (4)兼容 Vuex 依赖Promise。如果你支持的浏览器并没有实现 Promise (如 IE),那么你可以使用一个 polyfill 的库(如 es6-promis) 1.你可以通过 CDN 将其引入,window.Promise会自动可用: <script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.js"></script> 2.包管理器安装: npm install es6-promise --save //yarn add es6-promise 然后,将下列代码添加到你使用 Vuex 之前的一个地方: import 'es6-promise/auto' 三、使用 (1)使用介绍 1.搭建store实例 ①. 在创建vuex实例的地方引入vue、vuex,使用vuex: import Vue from 'vue'//引入vue import Vuex from 'vuex'//引入vuex Vue.use(Vuex); //使用 vuex ②. 如果你的actions中用到 store.dispath() ,要引入store import store from './store' //引入状态管理 store ③. new 一个 Vuex.Store 实例,并注册 state、mutations、actions、getters到Vuex.Store 实例中 ps. 你可以像上面那样将“属性和值”直接写在实例中,当代码量大时,你也可以分别写个.js文件,如下图: 然后引入到 store/index.js 注册到vuex实例中,如: 2.创建好 vuex.store 后,你需要在入口文件 main.js 中引入 store 并注册到 vue 实例中,这样就可以在任何组件使用store了: 3.在组件中使用: ①. 引入vuex中各属性对应的辅助函数: import {mapActions, mapState,mapGetters} from 'vuex' //注册 action 、 state 、getter ②. 使用store中的状态数据、方法: 使用方法有很多,这种事最简单实用的,更多可以查看官网学习:https://vuex.vuejs.org/zh/ (2)具体demo 来个简单易懂的计数 eg:store.js import Vue from 'vue'; import Vuex from 'vuex'; //引入 vuex import store from './store' //注册store Vue.use(Vuex); //使用 vuex export default new Vuex.Store({ state: { // 初始化状态 count: 0, someLists:[] }, mutations: { // 处理状态 increment(state, payload) { state.count += payload.step || 1; } }, actions: { // 提交改变后的状态 increment(context, param) { context.state.count += param.step; context.commit('increment', context.state.count)//提交改变后的state.count值 }, incrementStep({state, commit, rootState}) { if (rootState.count < 100) { store.dispatch('increment', {//调用increment()方法 step: 10 }) } } }, getters: { //处理列表项 someLists: state =>param=> { return state.someLists.filter(() => param.done) } } }) 使用时,eg: main.js: import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' //引入状态管理 store Vue.config.productionTip = false new Vue({ router, store,//注册store(这可以把 store 的实例注入所有的子组件) render: h => h(App) }).$mount('#app') views/home.vue: <template> <div class="home"> <!--在前端HTML页面中使用 count--> <HelloWorld :msg="count"/> <!--表单处理 双向绑定 count--> <input :value="count" @input="incrementStep"> </div> </template> <script> import HelloWorld from '@/components/HelloWorld.vue' import {mapActions, mapState,mapGetters} from 'vuex' //注册 action 和 state export default { name: 'home', computed: { //在这里映射 store.state.count,使用方法和 computed 里的其他属性一样 ...mapState([ 'count' ]), count () { return store.state.count } }, created() { this.incrementStep(); }, methods: { //在这里引入 action 里的方法,使用方法和 methods 里的其他方法一样 ...mapActions([ 'incrementStep' ]), // 使用对象展开运算符将 getter 混入 computed 对象中 ...mapGetters([ 'someLists' // ... ]) }, components: { HelloWorld } } </script> 运行结果: 博客地址:https://my.oschina.net/wangnian

优秀的个人博客,低调大师

PHP学习1——快速入门

主要内容: 搭建PHP开发环境 第一个helloworld程序 PHP(Hypertext Preprocessor)PHP超文本预处理程序,是一种嵌入HTML的脚本语言,运行在服务器。 搭建PHP开发环境 PHP开发环境主要3部分:服务器Apache,编程语言PHP,数据库MySQL。搭建开发环境,直接使用XAMPP集成安装就可以。 XAMPP=Apache+MySQL+PHP+Perl,X表示的是跨平台。 官网下载安装即可:https://www.apachefriends.org/zh_cn/index.html (安装太简单了,没有什么可说的,不过如果以前安装过tomcat,apache,MySQL等,xampp的部分功能可能不能使用,解决办法网上很多,不再赘言) 第一个helloworld程序 php可以使用note++进行编辑。 helloworld.php <?php echo "hello world!"; ?> 然后运行XAMPP的控制面板,点击Apache的start,绿色就是正常运行了。 然后将helloworld.php文件放在路径:C:\xampp\htdocs 浏览器输入:http://localhost/helloworld.php ok,我们的第一个PHP程序就完成了。

优秀的个人博客,低调大师

Dart In Action -Dart快速入门(-)

写在前面 本文基本上是将dart官网部分内容进行翻译,没兴趣的请出门左转至Dart的官网,有兴趣的同志请继续阅读本文。Flutter教程在这里 1.什么是Dart? 以下来自百度百科: Dart是谷歌开发的计算机编程语言,后来被Ecma (ECMA-408)认定为标准 [1] 。它被用于web、服务器、移动应用 [2] 和物联网等领域的开发。它是宽松开源许可证(修改的BSD证书)下的开源软件。 Dart是面向对象的、类定义的、单继承的语言。它的语法类似C语言,可以转译为JavaScript,支持接口(interfaces)、混入(mixins)、抽象类(abstract classes)、具体化泛型(reified generics)、可选类型(optional typing)和sound type system。 事实上,Dart被抛弃过,但Dart这门语言已经发展到2.x了。 2.为什么要学Dart? 大千世界,语言不计其数,这次我们要学习的为是Dart?答案是因为google推出的全新的跨平台UI框架--Flutter,而Flutter所使用的语言便是Dart。 3.初识Dart 以下代码展示了使用频率非常高并且最基础的Dart语法: // 定义一个函数. printInteger(int aNumber) { print('The number is $aNumber.'); // 将内容输出至控制台. } //和很多语言一样,dart选择了main()作为程序入口. main() { var number = 42; // 声明并且定义一个变量. printInteger(number); // 调用一个函数. } 4.Dart的注释 Dart的注释分为三种,举例说明: 单行注释 以//以为开始。在//和行尾之前的内容会被Dart编译器忽略掉。 void main() { // TODO: refactor into an AbstractLlamaGreetingFactory? print('Welcome to my Llama farm!'); } 多行注释 以/*开始并以*/为结束。/*和*/之前的内容会被Dart编译器忽略掉(除非注释是文档注释,下小节会介绍文档注释)。多行注释是可以嵌套的。 void main() { /* * This is a lot of work. Consider raising chickens. Llama larry = Llama(); larry.feed(); larry.exercise(); larry.clean(); */ } 3. 文档注释 文档注释是多行或者单行注释,以///或者/**开始。使用///注释连续的行与多行文档注释有相同的效果。 在文档注释中,Dart编译器会忽略所有文本,除非它被括在括号中。使用方括号,可以引用类、方法、字段、顶级变量、函数和参数。括号中的名称在文档化的程序元素的词法范围中解析。 /// A domesticated South American camelid (Lama glama). /// /// Andean cultures have used llamas as meat and pack /// animals since pre-Hispanic times. class Llama { String name; /// Feeds your llama [Food]. /// /// The typical llama eats one bale of hay per week. void feed(Food food) { // ... } /// Exercises your llama with an [activity] for /// [timeLimit] minutes. void exercise(Activity activity, int timeLimit) { // ... } } 在生成的文档中,[Food]成为指向Food类的API文档的链接。使用这个SDK可以解析Dart代码以及生HTML文档。 第一节结束

优秀的个人博客,低调大师

OpenStack快速入门-queens版本

系统内存要求8G,硬盘100G,操作系统可以连接公网,单网卡即可满足操作。本次实验使用了2块网卡。 操作系统版本:(最小化安装)CentOS Linux release 7.4.1708 (Core)openstack-queens版本centos-release-openstack-queens-1-1.el7.centos.x86_64 1.系统配置:关闭不需要服务:setenforce 0sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/configsystemctl disable firewalld systemctl stop firewalld systemctl disable NetworkManager systemctl stop NetworkManager 2.OpenStack软件安装:yum install centos-release-openstack-queens.x86_64 -y(安装openstack的yum 源)yum install -y openstack-packstackyum install python-pip -ypackstack --allinone (本次安装环境所有组建部署在一个节点。) 3,登陆dashboardhttp://192.168.0.78密码记录在keystonerc_admin文件中: unset OS_SERVICE_TOKEN export OS_USERNAME=admin export OS_PASSWORD=a8e90bb181da420e export OS_AUTH_URL=http://192.168.0.79:5000/v2.0 export PS1='[\u@\h \W(keystone_admin)]\$ ' export OS_TENANT_NAME=admin export OS_REGION_NAME=RegionOne

优秀的个人博客,低调大师

Docker 快速入门之 Dockerfile

在容器环境中,我们可以通过容器创建一个我们自定义过的镜像,那么我们是否可以直接通过基础的镜像直接自定义镜像呢?答案当然是可以的,在 Docker 中我们可以从名为Dockerfile的文件中读取指令并且自动构建镜像。在本文中,将介绍 Dockerfile 的基本语法以及基本知识。 1 Dockerfile 是什么? Dockerfile 其实是一份文本文档,里面包含了用户可以用来操作镜像的一些指令。通过顺序执行这些指令,最后得到一个自定义的镜像,这有点类似于我们的shell script。 2 Dockerfile 示例 接下来先看一个 Dockerfile 示例: FROM centos LABEL maintainer="Locez <locez@locez.com>" ENV TEST="This is a test env" COPY nginx.repo /etc/yum.repos.d/nginx.repo RUN yum update -y && \ yum install -y nginx COPY nginx.conf /etc/nginx/nginx.conf COPY index.html /usr/share/nginx/html/index.html COPY index_files/ /usr/share/nginx/html/index_files/ EXPOSE 80 CMD ["/usr/sbin/nginx","-g","daemon off;"] 在上面我们可以看到 Dockerfile 中的一些指令,通过名称我们也可以猜到这些指令大概是干嘛的,其中有一些对文件的操作,因此我们先来看看用于存放 Dockerfile 的这个目录的目录结构: # tree . . ├── Dockerfile ├── index_files │ ├── 145049z4og8xyjhx4xy8go.jpg │ ├── 222746e5vh38d7ey3leyps.jpg │ ├── 88x31.png │ ├── archlinux-splash.png │ ├── bdshare.css │ ├── Best-Linux-Markdown-Editors.png │ ├── core.js │ ├── docker-icon.jpg │ ├── hadoop-pic1.png │ ├── jquery_002.js │ ├── jquery.css │ ├── jquery.js │ ├── MathJax.js │ ├── pic.gif │ ├── raspberrypiraspberry-pi-logo.jpg │ ├── script.js │ ├── scrollup.png │ ├── share.js │ ├── style.css │ └── z_stat.js ├── index.html ├── nginx.conf └── nginx.repo 1 directory, 24 files 构建镜像 在当前目录下执行以下命令构建镜像: # docker build -t locez/nginx . Sending build context to Docker daemon 1.851 MB Step 1/10 : FROM centos ---> 196e0ce0c9fb Step 2/10 : LABEL maintainer "Locez <locez@locez.com>" ---> Using cache ---> 9bba3042bcdb Step 3/10 : ENV TEST "This is a test env" ---> Using cache ---> c0ffe95ea0c5 Step 4/10 : COPY nginx.repo /etc/yum.repos.d/nginx.repo ---> Using cache ---> bb6ee4c30d56 Step 5/10 : RUN yum update -y && yum install -y nginx ---> Using cache ---> 6d46b41099c3 Step 6/10 : COPY nginx.conf /etc/nginx/nginx.conf ---> Using cache ---> cfe908390aae Step 7/10 : COPY index.html /usr/share/nginx/html/index.html ---> Using cache ---> 21729476079d Step 8/10 : COPY index_files/ /usr/share/nginx/html/index_files/ ---> Using cache ---> 662f06ec7b46 Step 9/10 : EXPOSE 80 ---> Using cache ---> 30db5a889d0a Step 10/10 : CMD /usr/sbin/nginx -g daemon off; ---> Using cache ---> d29b9d4036d2 Successfully built d29b9d4036d2 然后用该镜像启动容器: # docker run -d -it --rm --name test-nginx -p 8080:80 locez/nginx e06fd991ca1b202e08cf1578f8046355fcbba10dd9a90e11d43282f3a1e36d29 用浏览器访问http://localhost:8080/即可看到部署的内容。 3 Dockerfile 指令解释 Dockerfile 支持FROM,RUN,CMD,LABEL,EXPOSE,ENV,ADD,COPY,ENTRYPOINT,VOLUME,USER,WORKDIR,ARG,ONBUILD,SHELL等指令,这里只选择常用的几个进行讲解,可结合上面的示例进行理解。其它的请自行查阅官方文档。 3.1 FROM FROM指令用于指定要操作的基础镜像,因为在我们构建我们自己的镜像的时候需要一个基础镜像。 语法: FROM <image> [AS <name>] FROM <image>[:<tag>] [AS <name>] 其中[AS <name>]为指定一个名称,在一个 Dockerfile 中多次使用FROM时如有需要,可用COPY --from=<name|index>语法进行复制。 3.2 RUN RUN指令用于执行命令,并且是在新的一层上执行,并把执行后的结果提交,也就是生成新的一层。基于这个问题,我们在使用RUN指令时应该尽可能的把要执行的命令一次写完,以减少最后生成的镜像的层数。语法: RUN <command> RUN ["executable", "param1", "param2"] 3.3 CMD CMD指令用于给容器启动时指定一个用于执行的命令,例如上例中的 nginx 启动命令。 语法: CMD ["executable","param1","param2"] CMD ["param1","param2"] ### 用于给 ENTRYPOINT 指令提供默认参数 CMD command param1 param2 3.4 LABEL LABEL指令用于为镜像指定标签,可用docker inspect命令查看。可用来代替被舍弃的MAINTAINER命令。语法: LABEL <key>=<value> <key>=<value> <key>=<value> ... 3.5 EXPOSE EXPOSE指令用于告诉 Docker 容器监听的特殊端口,但是此时端口还没有暴露给 host ,只有当在运行一个容器显式用参数-p或者-P的时候才会暴露端口。语法: EXPOSE <port> [<port>/<protocol>...] 3.6 ENV ENV指令用于设定环境变量。语法: ENV <key> <value> ENV <key>=<value> ... 3.7 ADD ADD指令用于复制新文件,目录,远程文件到容器中。其中<src>可以为文件,目录,URL,若为可解压文件,在复制后会解压。语法: ADD <src>... <dest> ADD ["<src>",... "<dest>"] 3.8 COPY COPY指令与ADD指令非常相似,但COPY比较直观且简单,它只支持本地的文件以及目录的复制,不像ADD指令可以远程获取文件并解压。语法: COPY <src>... <dest> COPY ["<src>",... "<dest>"] 3.9 ENTRYPOINT ENTRYPOINT指令也跟CMD指令相似,用于指定容器启动时执行的命令。当使用ENTRYPOINT指令时,可用CMD命令配合,这样在启动容器时,可以对CMD指令写入的参数进行覆盖。语法: ENTRYPOINT ["executable", "param1", "param2"] 样例: ENTRYPOINT ["top","-b"] CMD ["-c"] 上面的-c参数可以在启动时覆盖docker run -it --rm --name test top -H。 如果要覆盖ENTRYPOINT指令则用--entrypoint参数启动容器。 3.10 VOLUME VOLUME指令用于为容器创建一个挂载点,这个挂载点可以用来挂载本地文件/文件夹也可以用来挂载数据卷。其中若在启动一个新容器时没有指定挂载目录,则会自动创建一个数据卷,当容器被销毁时,数据卷如果没有被其它容器引用则会被删除。语法: VOLUME ["/data1","/data2"] 3.11 USER USER指令用于设置执行RUN,CMD,ENTRYPOINT等指令的用户以及用户组。默认为root用户。语法: USER <user>[:<group>] 3.12 WORKDIR WORKDIR指令用于设置RUN,CMD,ENTRYPOINT,COPY,ADD等指令的工作目录。语法: WORKDIR /path/to/workdir 4 总结 本文从一个具体的例子出发,讲述了如何利用 Dockerfile 构建镜像,然后解释了 Dockerfile 文件中的指令的语法,有关更多内容可访问官方文档。 5 参考资料 Dockerfile reference 原文发布时间: 2017-12-16 本文来自云栖社区合作伙伴“ Debian社区”,了解相关信息可以关注“ Debian社区”。

优秀的个人博客,低调大师

Mina2.0快速入门

MinaTimeServer.java 复制代码 package com.vista; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.charset.Charset; import org.apache.mina.core.service.IoAcceptor; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.filter.logging.LoggingFilter; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; public class MinaTimeServer { private static final int PORT = 6488; public static void main(String[] args) throws IOException { //监听即将到来的TCP连接 IoAcceptor acceptor = new NioSocketAcceptor(); acceptor.getFilterChain().addLast("logger", new LoggingFilter()); acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName("UTF-8")))); acceptor.setHandler(new TimeServerHandler()); acceptor.getSessionConfig().setReadBufferSize(2048); acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); acceptor.bind(new InetSocketAddress(PORT)); System.out.println("服务器启动"); } } 复制代码 TimeServerHandler.java 复制代码 package com.vista; import java.util.Date; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; public class TimeServerHandler extends IoHandlerAdapter { public void exceptionCaught(IoSession session, Throwable cause) throws Exception { cause.printStackTrace(); } public void messageReceived(IoSession session, Object message) throws Exception { String strMsg = message.toString(); if(strMsg.trim().equalsIgnoreCase("quit")) { session.close(); return; } Date date = new Date(); session.write(date.toString()); System.out.println("Message written"); } public void sessionIdle(IoSession session, IdleStatus status) throws Exception { System.out.println("IDLE"+session.getIdleCount(status)); } } 复制代码 测试: Client Output Server Output user@myhost:~> telnet 127.0.0.1 9123 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. hello Wed Oct 17 23:23:36 EDT 2007 quit Connection closed by foreign host. user@myhost:~> MINA Time server started. Message written... 参考资料:1,MINA v2.0 Quick Start Guide 本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2008/11/27/1342655.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Quartz 框架快速入门(一)

创建一个 Java 工程,引入几个 JAR 到工程中才能成功构建它们。首先,你需要 Quartz 的二进制版本,包的名字是 quartz-<version>.jar。Quartz 还需要几个第三方库;这依赖于你要用到框架的什么功能而定,Commons Digester 库可以在 <QUARTZ_HOME>/lib/core 和 <QUARTZ_HOME>/lib/optional 目录中找到。如果出现java.lang.NoClassDefFoundError: javax/transaction/UserTransaction的错误,解决办法是:引入jta.jar包,这个包在quartz-1.6.0/lib/build 下。 ·创建一个 Quartz Job 类 每一个 Quartz Job 必须有一个实现了 org.quartz.Job 接口的具体类。这个接口仅有一个要你在 Job 中实现的方法,execute(),方法 execute() 的原型如下: public void execute(JobExecutionContext context) throws JobExecutionException; 当 Quartz 调度器确定到时间要激发一个 Job 的时候,它就会生成一个 Job 实例,并调用这个实例的 execute() 方法。调度器只管调用 execute() 方法,而不关心执行的结果,除了在作业执行中出问题抛出的 org.quartz.JobExecutionException 异常。 下面是我们的第一个 Quartz job,它被设计来扫描一个目录中的文并显示文件的详细信息。 复制代码 package com.vista.quartz; import java.io.File; import java.io.FileFilter; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class ScanDirectoryJob implements Job { static Log logger = LogFactory.getLog(ScanDirectoryJob.class);//日志记录器 public void execute(JobExecutionContext context) throws JobExecutionException { //Every job has its own job detail JobDetail jobDetail = context.getJobDetail(); // The name is defined in the job definition String jobName = jobDetail.getName();//任务名称 // Log the time the job started logger.info(jobName + " fired at " + new Date());//记录任务开始执行的时间 // The directory to scan is stored in the job map JobDataMap dataMap = jobDetail.getJobDataMap();//任务所配置的数据映射表 String dirName = dataMap.getString("SCAN_DIR");//获取要扫描的目录 // Validate the required input if (dirName == null) {//所需要的扫描目录没有提供 throw new JobExecutionException( "Directory not configured" ); } // Make sure the directory exists File dir = new File(dirName); if (!dir.exists()) {//提供的是错误目录 throw new JobExecutionException( "Invalid Dir "+ dirName); } // Use FileFilter to get only XML files FileFilter filter = new FileExtensionFileFilter(".xml"); //只统计xml文件 File[] files = dir.listFiles(filter); if (files == null || files.length <= 0) {//目录下没有xml文件 logger.info("No XML files found in " + dir); // Return since there were no files return; } // The number of XML files int size = files.length; // Iterate through the files found for (int i = 0; i < size; i++) { File file = files[i]; // Log something interesting about each file. File aFile = file.getAbsoluteFile(); long fileSize = file.length(); String msg = aFile + " - Size: " + fileSize; logger.info(msg);//记录下文件的路径和大小 } } } 复制代码 当 Quartz 调用 execute() 方法,会传递一个 org.quartz.JobExecutionContext 上下文变量,里面封装有 Quartz 的运行时环境和当前正执行的 Job。通过 JobexecutionContext,你可以访问到调度器的信息,作业和作业上的触发器的信息,还有更多更多的信息。在代码中,JobExecutionContext 被用来访问 org.quartz.JobDetail 类,JobDetail 类持有 Job 的详细信息,包括为 Job 实例指定的名称,Job 所属组,Job 是否被持久化(易失性),和许多其他感兴趣的属性。 JobDetail 又持有一个指向 org.quartz.JobDataMap 的引用。JobDataMap 中有为指定 Job 配置的自定义属性。例如,在代码中我们从 JobDataMap 中获得欲扫描的目录名,我们可以在 ScanDirectoryJob 中硬编码这个目录名,但是这样的话我们难以重用这个 Job 来扫描别的目录了。在后面你将会看到目录是如何配置到 JobDataMap 的。 execute() 方法中剩下的就是标准 Java 代码了:获得目录名并创建一个 java.io.File 对象。它还对目录名作为简单的校验,确保是一个有效且存在的目录。接着调用 File 对象的 listFiles() 方法得到目录下的文件。还创建了一个 java.io.FileFilter 对象作为参数传递给 listFiles() 方法。org.quartzbook.cavaness.FileExtensionFileFilter 实现了 java.io.FileFilter 接口,它的作用是过滤掉目录仅返回 XML 文件。默认情况下,listFiles() 方法是返回目录中所有内容,不管是文件还是子目录,所以我们必须过滤一下,因为我们只对 XML 文件感兴趣。 FileExtensionFileFilter 被用来屏蔽名称中不含字符串 “.xml” 的文件。它还屏蔽了子目录--这些子目录原本会让 listFiles() 方法正常返回。过滤器提供了一种很便利的方式选择性的向你的 Quartz 作业提供它能接受的作为输入的文件 复制代码 package com.vista.quartz; import java.io.File; import java.io.FileFilter; public class FileExtensionFileFilter implements FileFilter { private String extension;//文件后缀 public FileExtensionFileFilter(String extension) { this.extension = extension; } public boolean accept(File file) {//只接受指定后缀的文件 // Lowercase the filename for easier comparison String lCaseFilename = file.getName().toLowerCase();//小写化 return (file.isFile() &&(lCaseFilename.indexOf(extension) > 0 )) ? true : false ; } } 复制代码 到目前为止,我们已经创建了一个 Quartz job,但还没有决定怎么处置它--明显地,我们需以某种方式为这个 Job 设置一个运行时间表。时间表可以是一次性的事件,或者我们可能会安装它在除周日之外的每个午夜执行。你即刻将会看到,Quartz Schduler 是框架的心脏与灵魂。所有的 Job 都通过 Schduler 注册;必要时,Scheduler 也会创建 Job 类的实例,并执行实例的 execute() 方法。 ·编程式安排一个 Quartz Job 所有的要 Quartz 来执行的作业必须通过调度器来注册。大多情况下,这会在调度器启动前做好。正如前面说过,这一操作也提供了声明式与编程式两种实现途径的选择。 因为每一个 Job 都必须用 Scheduler 来注册,所以先定义一个 JobDetail,并关联到这个 Scheduler 实例。 下面的程序提供了一个理解如何编程式安排一个 Job 很好的例子。代码首先调用 createScheduler() 方法从 Scheduler 工厂获取一个 Scheduler 的实例。得到 Scheduler 实例之后,把它传递给 schedulerJob() 方法,由它把 Job 同 Scheduler 进行关联。 首先,创建了我们想要运行的 Job 的 JobDetail 对象。JobDetail 构造器的参数中包含指派给 Job 的名称,逻辑组名,和实现 org.quartz.Job 接口的全限类名称。我们可以使用 JobDetail 的别的构造器。 在前面有说过,JobDetail 扮演着某一 Job 定义的角色。它带有 Job 实例的属性,能在运行时被所关联的 Job 访问到。其中在使用 JobDetail 时,的一个最重要的东西就是 JobDataMap,它被用来存放 Job 实例的状态和参数。在代码中,待扫描的目录名称就是通过 scheduleJob() 方法存入到 JobDataMap 中的。 Job 只是一个部分而已。注意我们没有在 JobDetail 对象中为 Job 设定执行日期和次数。这是 Quartz Trigger 该做的事。顾名思义,Trigger 的责任就是触发一个 Job 去执行。当用 Scheduler 注册一个 Job 的时候要创建一个 Trigger 与这个 Job 相关联。Quartz 提供了四种类型的 Trigger,但其中两种是最为常用的,它们就是在下面要用到的 SimpleTrigger 和 CronTrigger. SimpleTrigger 是两个之中简单的那个,它主要用来激发单事件的 Job,Trigger 在指定时间激发,并重复 n 次--两次激发时间之间的延时为 m,然后结束作业。CronTrigger 非常复杂且强大。它是基于通用的公历,当需要用一种较复杂的时间表去执行一个 Job 时用到。例如,四月至九月的每个星期一、星期三、或星期五的午夜。 为更简单的使用 Trigger,Quartz 包含了一个工具类,叫做 org.quartz.TriggerUtils. TriggerUtils 提供了许多便捷的方法简化了构造和配置 trigger. 本文的例子中有用的就是 TriggerUtils 类;SimpleTrigger 和 CronTrigger 会在后面用到。 正如你看到的那样,调用了 TriggerUtils 的方法 makeSecondlyTrigger() 来创建一个每10秒种激发一次的 trigger(实际是由 TriggerUtils 生成了一个 SimpleTrigger 实例,但是我们的代码并不想知道这些)。我们同样要给这个 trigger 实例一个名称并告诉它何时激发相应的 Job;与之关联的 Job 会立即启动,因为由方法 setStartTime() 设定的是当前时间 复制代码 package com.vista.quartz; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerUtils; import org.quartz.impl.StdSchedulerFactory; public class SimpleScheduler { static Log logger = LogFactory.getLog(SimpleScheduler.class); public static void main(String[] args) { SimpleScheduler simple = new SimpleScheduler(); try { // Create a Scheduler and schedule the Job Scheduler scheduler = simple.createScheduler(); simple.scheduleJob(scheduler); // Start the Scheduler running scheduler.start(); logger.info( "Scheduler started at " + new Date()); } catch (SchedulerException ex) { logger.error(ex); } } public Scheduler createScheduler() throws SchedulerException {//创建调度器 return StdSchedulerFactory.getDefaultScheduler(); } //Create and Schedule a ScanDirectoryJob with the Scheduler private void scheduleJob(Scheduler scheduler) throws SchedulerException { // Create a JobDetail for the Job JobDetail jobDetail = new JobDetail("ScanDirectory",Scheduler.DEFAULT_GROUP,ScanDirectoryJob.class); // Configure the directory to scan jobDetail.getJobDataMap().put("SCAN_DIR","D:\\Tomcat\\conf"); //set the JobDataMap that is associated with the Job. // Create a trigger that fires every 10 seconds, forever Trigger trigger = TriggerUtils.makeSecondlyTrigger(10);//每10秒触发一次 trigger.setName("scanTrigger"); // Start the trigger firing from now trigger.setStartTime(new Date());//设置第一次触发时间 // Associate the trigger with the job in the scheduler scheduler.scheduleJob(jobDetail, trigger); } } 复制代码 假如你有不只一个个 Job (你也许就是),你将需要为每一个 Job 创建各自的 JobDetail。每一个 JobDetail 必须通过 scheduleJob() 方法一一注册到 Scheduler 上。而如果你想重用了一个 Job 类,让它产生多个实例运行,那么你需要为每个实例都创建一个 JobDetail。例如,假如你想重用 ScanDirectoryJob 让它检查两个不同的目录,你需要创建并注册两个 JobDetail 实例 复制代码 package com.vista.quartz; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerUtils; import org.quartz.impl.StdSchedulerFactory; public class SimpleScheduler { static Log logger = LogFactory.getLog(SimpleScheduler.class); public static void main(String[] args) { SimpleScheduler simple = new SimpleScheduler(); try { // Create a Scheduler and schedule the Job Scheduler scheduler = simple.createScheduler(); // Jobs can be scheduled after Scheduler is running scheduler.start(); logger.info("Scheduler started at " + new Date()); // Schedule the first Job simple.scheduleJob(scheduler, "ScanDirectory1",ScanDirectoryJob.class,"D:\\conf1", 10); // Schedule the second Job simple.scheduleJob(scheduler, "ScanDirectory2",ScanDirectoryJob.class,"D:\\conf2 ", 15); } catch (SchedulerException ex) { logger.error(ex); } } public Scheduler createScheduler() throws SchedulerException {//创建调度器 return StdSchedulerFactory.getDefaultScheduler(); } private void scheduleJob(Scheduler scheduler, String jobName,Class jobClass, String scanDir, int scanInterval) throws SchedulerException { // Create a JobDetail for the Job JobDetail jobDetail = new JobDetail(jobName,Scheduler.DEFAULT_GROUP, jobClass); // Configure the directory to scan jobDetail.getJobDataMap().put("SCAN_DIR", scanDir); // Trigger that repeats every "scanInterval" secs forever Trigger trigger = TriggerUtils.makeSecondlyTrigger(scanInterval); trigger.setName(jobName + "-Trigger"); // Start the trigger firing from now trigger.setStartTime(new Date()); // Associate the trigger with the job in the scheduler scheduler.scheduleJob(jobDetail, trigger); } } 复制代码 本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2008/09/03/1282747.html,如需转载请自行联系原作者

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Apache Tomcat

Apache Tomcat

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。