使用docker搭建swoft 2.x环境

最近项目要使用到框架swoft,但是官方文档以及网上的各路教程均无法直接搭建成功,顾折腾了1天后,记录一下搭建过程:
1.首先VM上新建一个centos7.x的系统,参考:https://www.cnblogs.com/toutou/p/vmware_workstation_centos7.html
2.配置xshell连接虚拟机,参考:https://www.cnblogs.com/shireenlee4testing/p/9469650.html
3.安装docket

yum install docker -y
#安装成功后,启动docker
cd /bin
systemctl start docker
#检查状态
docker ps -a
#出现列表即启动成功

4.安装docker-compose
参考:https://blog.csdn.net/pushiqiang/article/details/78682323

sudo curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
#添加可执行权限
sudo chmod +x /usr/local/bin/docker-compose
#测试
docker-compose --version
#出现以下结果代表成功

docker-compose version 1.16.1, build 1719ceb

5.安装git(可跳过)
yum -y install git
6.关闭snlinux防火墙,参考:https://blog.51cto.com/bguncle/957315
7.拉取swoft项目:参考:https://www.cnblogs.com/a609251438/p/12098988.html

cd /home
#创建www目录
mkdir www
cd /www
git clone https://github.com/swoft-cloud/swoft
#如果上面连接无法拉取,使用下面这条
git clone git://github.com/swoft-cloud/swoft

8.修改docker-compose.yml配置文件

#进入项目目录
cd swoft
#先备份一个文件
cp docker-compose.yml docker-compose-back.yml
#编辑文件
vi docker-compose.yml
#在swoft代码块中添加下面三行
command: /bin/bash
tty: true
stdin_open: true
#并且注释restart: always

9.启动容器并进入:

docker-compose up -d

#查看容器id
docker ps -a

#获取到swoft对应的id或者名字,然后进入到容器内
docker exec -it 243c32535da7 /bin/bash
#或者 
docker exec -it swoft-srv /bin/bash

10.进入后到即为项目的根目录,查看.env文件,如果没有就拷贝一份
cp .env.example .env

11.执行composer安装

#先配置阿里云镜像源
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

composer install
#安装依赖成功后,测试是否安装成功
php bin/swoft -v
#出现版本信息即为成功


12.启动项目,参考:https://www.thinkphp.cn/extend/1372.html
php bin/swoft ws:start

Laravel使用migrate更新指定数据表

在项目开发中,数据表往往是分开多次建立的,因此后期使用artisan命令
php artisan migrate
往往会报错
SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'xxx' already exists
所以我们可以在database下新建目录new,然后把新建并且数据库中还没有对应表的Migration拷贝到该目录下,然后执行下面命令
php artisan migrate --path=database/new
如果执行还是不成功,可以执行
php artisan migrate:refresh --path=database/new

MySql执行过程、执行顺序、执行计划

前言:MySql是我们平常用的比较多的数据库,但是时间久了后总会或多或少忘记了一条sql完整的执行过程,今天查资料,重新理解了一下MySql语句执行的全过程。

导引:
一、执行过程
二、执行过程中的状态
三、执行的顺序
四、执行的计划
五、EXPLAIN的局限
六、总结

一、执行过程

MySql整体的执行过程如下图所示:

1.1:连接器

连接器的主要职责就是:

①负责与客户端的通信,是半双工模式,这就意味着某一固定时刻只能由客户端向服务器请求或者服务器向客户端发送数据,而不能同时进行,即同一时间只能进行一个动作

②验证请求用户的账户和密码是否正确,如果账户和密码错误,会报错:Access denied for user ‘root’@’localhost’ (using password: YES)

③如果用户的账户和密码验证通过,会在mysql自带的权限表中查询当前用户的权限:

mysql中存在4个控制权限的表,分别为user表,db表,tables_priv表,columns_priv表,mysql权限表的验证过程为:

1:User表:存放用户账户信息以及全局级别(所有数据库)权限,决定了来自哪些主机的哪些用户可以访问数据库实例;
Db表:存放数据库级别的权限,决定了来自哪些主机的哪些用户可以访问此数据库;
Tables_priv表:存放表级别的权限,决定了来自哪些主机的哪些用户可以访问数据库的这个表;
Columns_priv表:存放列级别的权限,决定了来自哪些主机的哪些用户可以访问数据库表的这个字段;
Procs_priv表:存放存储过程和函数级别的权限。

2:先从user表中的Host,User,Password这3个字段中判断连接的ip、用户名、密码是否存在,存在则通过验证。

3:通过身份认证后,进行权限分配,按照user,db,tables_priv,columns_priv的顺序进行验证。即先检查全局权限表user,如果user中对应的权限为Y,则此用户对所有数据库的权限都为Y,将不再检查db, tables_priv,columns_priv;如果为N,则到db表中检查此用户对应的具体数据库,并得到db中为Y的权限;如果db中为N,则检查tables_priv中此数据库对应的具体表,取得表中的权限Y,以此类推

4:如果在任何一个过程中权限验证不通过,都会报错

1.2:缓存

mysql的缓存主要的作用是为了提升查询的效率,缓存以key和value的哈希表形式存储,key是具体的sql语句,value是结果的集合。如果无法命中缓存,就继续走到分析器的的一步,如果命中缓存就直接返回给客户端 。不过需要注意的是在mysql的8.0版本以后,缓存被官方删除掉了。之所以删除掉,是因为查询缓存的失效非常频繁,如果在一个写多读少的环境中,缓存会频繁的新增和失效。对于某些更新压力大的数据库来说,查询缓存的命中率会非常低,mysql为了维护缓存可能会出现一定的伸缩性的问题,目前在5.6的版本中已经默认关闭了,比较推荐的一种做法是将缓存放在客户端,性能大概会提升5倍左右。

1.3:分析器

分析器的主要作用是将客户端发过来的sql语句进行分析,这将包括预处理与解析过程,在这个阶段会解析sql语句的语义,并进行关键词和非关键词进行提取、解析,并组成一个解析树。具体的关键词包括不限定于以下:select/update/delete/or/in/where/group by/having/count/limit等.如果分析到语法错误,会直接给客户端抛出异常:ERROR:You have an error in your SQL syntax.

比如:select * from user where userId =1234;

在分析器中就通过语义规则器将select from where这些关键词提取和匹配出来,mysql会自动判断关键词和非关键词,将用户的匹配字段和自定义语句识别出来。这个阶段也会做一些校验:比如校验当前数据库是否存在user表,同时假如User表中不存在userId这个字段同样会报错:unknown column in field list.

1.4:优化器

能够进入到优化器阶段表示sql是符合mysql的标准语义规则的并且可以执行的,此阶段主要是进行sql语句的优化,会根据执行计划进行最优的选择,匹配合适的索引,选择最佳的执行方案。比如一个典型的例子是这样的:

表T,对A、B、C列建立联合索引,在进行查询的时候,当sql查询到的结果是:select xx where B=x and A=x and C=x.很多人会以为是用不到索引的,但其实会用到,虽然索引必须符合最左原则才能使用,但是本质上,优化器会自动将这条sql优化为:where A=x and B=x and C=X,这种优化会为了底层能够匹配到索引,同时在这个阶段是自动按照执行计划进行预处理,mysql会计算各个执行方法的最佳时间,最终确定一条执行的sql交给最后的执行器。

1.5:执行器

在执行器的阶段,此时会调用存储引擎的API,API会调用存储引擎,主要有一下存储的引擎,不过常用的还是myisam和innodb:

引擎以前的名字叫做:表处理器(更直观)负责对具体的数据文件进行操作,对sql的语义比如select或者update进行分析,执行具体的操作。在执行完以后会将具体的操作记录到binlog中,需要注意的一点是:select不会记录到binlog中,只有update/delete/insert才会记录到binlog中。而update会采用两阶段提交的方式,记录都redolog中。

二、执行过程中的状态

可以通过命令:show full processlist,展示所有的处理进程,主要包含了以下的状态,表示服务器处理客户端的状态,状态包含了从客户端发起请求到后台服务器处理的过程,包括加锁的过程、统计存储引擎的信息,排序数据、搜索中间表、发送数据等。囊括了所有的mysql的所有状态,其中具体的含义如下图

三、执行的顺序

事实上,sql并不是按照我们的书写顺序来从前往后、左往右依次执行的,它是按照固定的顺序解析的,主要的作用就是从上一个阶段的执行返回结果来提供给下一阶段使用,sql在执行的过程中会有不同的临时中间表,一般是按照如下顺序:
例子:
select distinct s.id from T t join S s on t.id=s.id where t.name="Yrion" group by t.mobile having count(*)>2 order by s.create_time limit 5;

3.1:from

第一步就是选择出from关键词后面跟的表,这也是sql执行的第一步:表示要从数据库中执行哪张表。

实例说明:在这个例子中就是首先从数据库中找到表T

3.2:join on

join是表示要关联的表,on是连接的条件,先通过join确认要关联的表,再通过on确认关联条件。通过from和join on选择出需要执行的数据库表T和S,产生笛卡尔积,生成T和S合并的临时中间表Temp1。on:确定表的绑定关系,通过on产生临时中间表Temp2。

实例说明:找到表S,生成临时中间表Temp1,然后找到表T的id和S的id相同的部分组成成表Temp2,Temp2里面包含着T和Sid相等的所有数据。

3.3:where

where表示筛选,根据where后面的条件进行过滤,按照指定的字段的值(如果有and连接符会进行联合筛选)从临时中间表Temp2中筛选需要的数据,注意如果在此阶段找不到数据,会直接返回客户端,不会往下进行.这个过程会生成一个临时中间表Temp3。注意在where中不可以使用聚合函数,聚合函数主要是(min\max\count\sum等函数)。

实例说明:在temp2临时表集合中找到T表的name=”Yrion”的数据,找到数据后会成临时中间表Temp3,temp3里包含name列为”Yrion”的所有表数据。

3.4:group by

group by是进行分组,对where条件过滤后的临时表Temp3按照固定的字段进行分组,产生临时中间表Temp4,这个过程只是数据的顺序发生改变,而数据总量不会变化,表中的数据以组的形式存在。

实例说明:在temp3表数据中对mobile进行分组,查找出mobile一样的数据,然后放到一起,产生temp4临时表。

3.5:Having

对临时中间表Temp4进行聚合,这里可以为count等计数,然后产生中间表Temp5,在此阶段可以使用select中的别名。

实例说明:在temp4临时表中找出条数大于2的数据,如果小于2直接被舍弃掉,然后生成临时中间表temp5

3.6:select

对分组聚合完的表挑选出需要查询的数据,如果为*会解析为所有数据,此时会产生中间表Temp6

实例说明:在此阶段就是对temp5临时聚合表中S表中的id进行筛选产生Temp6,此时temp6就只包含有s表的id列数据,并且name=”Yrion”,通过mobile分组数量大于2的数据。

3.7:Distinct

distinct对所有的数据进行去重,此时如果有min、max函数会执行字段函数计算,然后产生临时表Temp7。

实例说明:此阶段对temp5中的数据进行去重,引擎API会调用去重函数进行数据的过滤,最终只保留id第一次出现的那条数据,然后产生临时中间表temp7。

3.8:order by

会根据Temp7进行顺序排列或者逆序排列,然后插入临时中间表Temp8,这个过程比较耗费资源。

实例说明:这段会将所有temp7临时表中的数据按照创建时间(create_time)进行排序,这个过程也不会有列或者行损失。

3.9:limit

limit对中间表Temp8进行分页,产生临时中间表Temp9,返回给客户端。

实例说明:在temp7中排好序的数据,然后取前五条插入到Temp9这个临时表中,最终返回给客户端

ps:实际上这个过程也并不是绝对这样的,中间mysql会有部分的优化以达到最佳的优化效果,比如在select筛选出找到的数据集。

四、执行的计划

4.1:什么是执行计划

执行计划就是sql的执行查询的顺序,以及如何使用索引查询,返回的结果集的行数

4.2:执行计划的内容

id

包含一组数字,表示查询中执行select子句或操作表的顺序。id一样,按照顺序执行;id越大,执行的优先级就越高(如子查询)

id相同,执行顺序由上至下


如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行


id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行

select_type

表示查询中每个select子句的类型(简单 OR复杂)

a.SIMPLE:查询中不包含子查询或者UNION
b.查询中若包含任何复杂的子部分,最外层查询则被标记为:PRIMARY
c.在SELECT或WHERE列表中包含了子查询,该子查询被标记为:SUBQUERY
d.在FROM列表中包含的子查询被标记为:DERIVED(衍生)
e.若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在 FROM子句的子查询中,外层SELECT将被标记为:DERIVED
f.从UNION表获取结果的SELECT被标记为:UNION RESULT

type

MySQL在表中找到所需行的方式,又称“访问类型”,常见类型如下:

由左至右,由最差到最好

  • a.ALL:
    Full Table Scan, MySQL将遍历全表以找到匹配的行

  • b.index:
    Full Index Scan,index与ALL区别为index类型只遍历索引树

  • c.range:
    索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行,常见于between、<、>等的查询,但是要控制查询的时间范围,一般查询数据不要超过数据总数的 15%

range访问类型的不同形式的索引访问性能差异

  • d.ref:
    非唯一性索引扫描,返回匹配某个单独值的所有行。常见于使用非唯一索引即唯一索引的非唯一前缀进行的查找。

  • e.eq_ref:
    唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描

  • f.const、system:
    当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量


system是const类型的特例,当查询的表只有一行的情况下, 使用system

  • g.NULL:
    MySQL在优化过程中分解语句,执行时甚至不用访问表或索引

possible_keys

指出MySQL能使用哪个索引在表中找到行,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用

key

显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。
TIPS:查询中若使用了覆盖索引,则该索引仅出现在key列表中。
覆盖索引:查询数据只需要通过索引就可以查询出,如55万条数据,使用索引,立刻可以查询出 2000条数据,同时Extra字段是Using index

key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度


key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的

ref

表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

rows

表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数

Extra

包含不适合在其他列中显示但十分重要的额外信息

  • a.Using index

该值表示相应的select操作中使用了覆盖索引(Covering Index)


TIPS:覆盖索引(Covering Index)

MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件

包含所有满足查询需要的数据的索引称为 覆盖索引(Covering Index)

注意:
如果要使用覆盖索引,一定要注意select列表中只取出需要的列,不可select *,因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降

  • b.Using where

表示MySQL服务器在存储引擎受到记录后进行“后过滤”(Post-filter),
如果查询未能使用索引,Using where的作用只是提醒我们MySQL将用where子句来过滤结果集

  • c.Using temporary

表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询

  • d.Using filesort

MySQL中无法利用索引完成的排序操作称为“文件排序”
很多场景都是索引是一个字段,order by 排序的字段与索引字段不一致,导致的Using fileSort;
此时可以给排序字段和where条件字段,添加为组合索引,同时保证索引查询的数据不超过总量的15%,避免fileSort

注:回表的含义是,先根据索引查询数据,然后在根据确定的数据id和查询条件再去查询具体的数据的过程

五、EXPLAIN的局限

•EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况
•EXPLAIN不考虑各种Cache
•EXPLAIN不能显示MySQL在执行查询时所作的优化工作
•部分统计信息是估算的,并非精确值
•EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划

六、总结

本文从MySql执行过程、执行过程的状态,到MySql的执行顺序、MySql的执行计划,一路做了详细的总结,理解这些有助于提高我们对mysql的总体理解和把握,有助于我们对sql语句进行优化,以及明白mysql中的sql语句从写出来到最终执行的轨迹。希望能帮到大家。

参考链接:
1.mysql执行过程以及顺序
2.MySql执行顺序及执行计划
3.MySQL执行计划解读

meta加了缓存静态文件不生效

迫于网站首页打开缓慢,在头部加了缓存静态资源文件的代码,然而发现不生效

<meta http-equiv="Cache-Control" content="max-age=7200" />

打开控制台发现缓存状态为:

Cache-Control:no-store,no-cache,must-revalidate;

Pragma: no-cache;

初步怀疑是服务器配置问题,一番度娘后发现是php.ini配置的问题,打开配置文件

#找到
session.cache_limiter
#默认为nocache,修改为none

修改后重载配置,即可生效

参考链接:
1:http://www.zxsdw.com/index.php/archives/926/
2:https://blog.csdn.net/a624193873/article/details/103130043

TP3.2自定义函数过滤单引号防止sql注入

TP3.2自带的I方法使用的是htmlspecialchars过滤,但是默认是不转移单引号的,这就会增加sql注入的风险,因此我们可以按照TP3官方文档,自定义函数,自行过滤

#首先在function文件中添加方法
#\Application\Common\Common\function.php
function htmlentities_custom(str){
    return htmlentities(str, ENT_QUOTES);
}

#然后在配置DEFAULT_FILTER中添加该自定义方法
'DEFAULT_FILTER' =>  'htmlentities_custom',

#添加完后,清除缓存,即可过滤单引号

记一次数据库误删后恢复过程

故事背景:搭建测试站点时候,忘记修改数据库连接,删除数据时候直接把正式数据一锅端了。
跑路是不可能跑路的,那只能想办法恢复数据了;
登上服务器一看,上一次备份已经是3天前了。好家伙,自动备份也没设。╮(╯▽╰)╭
但是恢复还是可以恢复的,现在理清思路:
1.先把现在误删后的数据库备份一份(还是有部分记录没删的)
2.找到服务器上mysql二进制文件,查找上一次备份对应节点,和刚才删除操作前一个节点位置
3.找到到对应节点后,导入3天前备份,并利用二进制文件恢复这3天数据;
理清思路后,开干!

首先,我们把数据库上的mysql-bin拷贝到本地-.-
内心os:不然在服务器上看几百M的文件真的耗不起……….

cd /www/server/data  //cd到mysql data目录
ls(ll)指令查看data目录下文件信息  //cd到data目录后执行该指令

可见,最新的mysql-bin文件是000005
到此为止我们已经完美的查询到了mysql-bin文件,尔后我们对所需要的文件进行转存,代码如下:

//先进入到mysql bin目录
cd /www/server/mysql/bin
//将000005文件转存到/root目录中,且重命名为log05.log
./mysqlbinlog /www/server/data/mysql-bin.000005 > /root/log05.log

获取到log日志后,下载到本地,并找到对应节点后,先把3天前的备份导入
然后就可以开始恢复数据了

./mysqlbinlog  --start-position=31758225 --stop-position=53741915 -d youdatasbase /www/server/data/mysql-bin.000005|mysql -uroot -pxxx youdatasbase

自此,数据恢复完成

php 7.2 安装 mcrypt 扩展: mcrypt 扩展从 php 7.1.0 开始废弃;自 php 7.2.0 起,会移到 pecl

升级 php 7.2 后,使用微信提供的加解密代码时,提示 call to undefined function mcrypt_module_open() ;大脑疯狂运转1秒钟后,得出结论:php 7.2的扩展有变动;查阅相关资料知晓,mcrypt 扩展从 php 7.1.0 开始废弃;自 php 7.2.0 起,会移到 pecl。还好,安装过程不复杂。

环境:centos 7

1.yum 安装依赖包:

yum install libmcrypt libmcrypt-devel mcrypt mhash

2.在 php 官网下载 mcrypt 包,php 扩展官网

# wget http://pecl.php.net/get/mcrypt-1.0.1.tgz

# tar xf mcrypt-1.0.1.tgz

# cd mcrypt-1.0.1

3.编译安装 mcrypt

进入到php安装目录

# /usr/local/php/bin/phpize

# ./configure –with-php-config=/usr/local/php/bin/php-config && make && make install

4.在php.ini加上扩展即可

extension=mcrypt.so

5.重启 php-fpm

/etc/init.d/php-fpm restart #可以根据实际情况修改,你也可能是/usr/local/php/sbin/php-fpm

composer安装laravel Changed current directory to没反应

根据官方手册执行composer global require “laravel/installer”

显示Changed current directory to C:/Users/Administrator/AppData/Roaming/Composer

然后没反应了,因为去国外的镜像地址下载了。

应该先将composer换成国内镜像

composer config -g repo.packagist composer https://packagist.phpcomposer.com

Windows下安装composer出错的原因

需要安装Composer,安装地址:https://pkg.phpcomposer.com/#how-to-install-composer

1.执行了代码1.执行了代码

php -r "copy('https://install.phpcomposer.com/installer', 'composer-setup.php');"

2.出错如下:

Warning: copy(): SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify faile
d in Command line code on line 1

Warning: copy(): Failed to enable crypto in Command line code on line 1

Warning: copy(https://install.phpcomposer.com/installer): failed to open stream:
 operation failed in Command line code on line 1

3.解决的方法

a.原因是在于没有CA证书导致的,下载CA证书:https://curl.haxx.se/docs/caextract.html

b.修改PHP.ini,指定下载的cacert.pem路径

openssl.cafile=D:\server\composer\certs\cacert.pem

利用第三方翻译API翻译整个网站

最新一个项目需要全站翻译,但是有没有做国际化。改起来比较麻烦。所以就想有没有什么好的方法可以直接翻译整个网站。

思路:

利用第三方翻译API,把网页中所有的中文发过去,拿到返回值之后把所有中文替换成英文。

1 百度翻译开发平台注册

官网有很详细的问题,一步一步走就OK啦。附上它的官网。
http://api.fanyi.baidu.com/api/trans/product/index

2 怎么使用

直接上代码吧,注释也还算清晰。

<!doctype html>
<head>
    <meta charset="utf-8" />
</head>
<body>
    <div>测试
        <div>橘子</div>
    </div>
    <div>梨</div>
    <div>测试</div>
    <div>零食</div>
    <div>水果</div>
    <div>上班</div>
    <script src="js/jquery-1.7.1.min.js" type="text/javascript" charset="utf-8"></script>
    <script src="./js/md5.js"></script>
    <script type="text/javascript">
        transformLanguage('en');
        function transformLanguage(newLanguage) {
            // 获取所有dom元素中文
            let transformStr = '';
            // 获取所有dom元素
            function getChildDom(dom, type, data = {}) {
                if(type == 'read') {
                    [...dom.children].forEach(v => {
                        // 判断中文
                        // /^[\u0391-\uFFE5]+/
                        let re= /[\u4e00-\u9fa5]/g;
                        // 防止某些标签有内容并且有标签 ,或者有空格                        let vHtml =(v).contents().filter(function (index, content) {return content.nodeType === 3}).text().trim();
                        // 跳过script标签
                        if (re.test(vHtml) && v.tagName != 'SCRIPT') {
                            transformStr += `{vHtml},`
                        }
                        // 递归获取元素
                        getChildDom(v, type, data);
                    })
                }else {
                    let transOld = data.trans_result[0].src.split(',');
                    let transNew = data.trans_result[0].dst.split(',');
                    [...dom.children].forEach(v => {
                        // 判断中文
                        // /^[\u0391-\uFFE5]+/
                        let re= /[\u4e00-\u9fa5]/g;
                        let vHtml = (v).contents().filter(function (index, content) {return content.nodeType === 3}).text().trim();
                        // 跳过script标签
                        if (re.test(vHtml) && v.tagName != 'SCRIPT') {
                            // 防止标签里面还有标签,所以只替换里面的html,使用replace(v).html(
                                (v).html().replace(
                                transOld[transOld.findIndex(arrList => arrList == vHtml)]
                                ,
                                transNew[transOld.findIndex(arrList => arrList == vHtml)]
                                )
                            )
                        }
                        // 递归获取元素
                        getChildDom(v, type, data);
                    })
                }
            }
            getChildDom(document,'read');
            getTranslateData();
            // 获取翻译
            function getTranslateData() {
                let appid = '*******************';   // 百度翻译API的appid
                let key = '*********************';   // 百度翻译API的key
                let salt = (new Date).getTime();
                let query = transformStr;
                let from = 'zh';
                let to = newLanguage;
                let str1 = appid + query + salt + key;
                let sign = MD5(str1);.ajax({
                    url: 'http://api.fanyi.baidu.com/api/trans/vip/translate',
                    type: 'get',
                    dataType: 'jsonp',
                    data: {
                        q: query,
                        appid: appid,
                        salt: salt,
                        from: from,
                        to: to,
                        sign: sign
                    },
                    success: function(data) {
                        data.trans_result && getChildDom(document,'write',data);
                        console.log(data);
                    }
                });
            }
        }
    </script>
</body>

网上随便找一个jq,和md5.js放进去就好啦。
参考:利用第三方翻译API翻译整个网站