一、脚本、uuid
1、脚本
(1)脚本是一种使用特定描述性语言编写的可执行文件,通常以文本形式保存
(2)脚本包含了一系列指令,这些指令由计算机的解释器或脚本引擎执行,用于自动化执行常规任务,如自动化数据处理、网页开发等
2、UUID,通用唯一识别码
(1)英文全称,Universally Unique Identifier
(2)一种标准化128位标识符,保证全球范围内的唯一性(冲突概率极低)
(3)JS中引用UUID,
npm install uuid
import { v4 as uuidv4 } from 'uuid';
const id = uuidv4();
二、环境
1、生产环境:服务器
2、开发环境
(1)前端:node
(2)后台:ubuntu
3、window环境下的上传工具
(1)MobaXterm
(2)xshell
4、ubuntu环境下的上传命令
scp -r localfile.txt username@192.168.0.1:/home/username/
(1)scp是命令,-r是参数
(2)localfile.txt 是文件的路径和文件名
(3)username是服务器账号
(4)192.168.0.1是要上传的服务器ip地址
(5)/home/username/是要拷入的文件夹路径
三、服务器
1、安装服务器系统
(1)给服务器--插上--系统U盘
(2)重启服务器:拔、插服务器电源(服务器自动安装系统,安装成功时,服务器自动断电)
(3)从服务器--拔掉--系统U盘
(4)再次重启服务器:拔、插服务器电源
2、登陆服务器
(1)用“网线”把“我的电脑”和“服务器”进行物理连接
(2)在“终端工具”上,用“终端工具”的用户名和密码登录“终端工具”
(3)在“终端工具”上,用“服务器”的用户名和密码登录“服务器”
3、给服务器配置IP
(1)root@CyOS:/root# ifconfig mgmt0 192.168.10.156(给接口配置IP。它有65535个端口,ifconfig(配置))mgmt0(接口))
(2)按enter键,IP配置成功
4、启动服务器
(1)root@CyOS:/root# cd /usr/local/audit-web
(2)root@CyOS:/usr/local/audit-web# python manage.py runserver
5、辅助步骤
(1)Ctrl+C(退出进程)
(2)root@CyOS:/usr/local/audit-web# pkill -f python -9(杀死python进程,出现Address already in use时,需要用到这个命令)
(3)root@CyOS:/usr/local/audit-web# pkill -f uwsgi -9(杀死名为uwsgi的进程,-f匹配所有参数列表,-9强制)
(4)root@CyOS:/usr/local/audit-web# iptables -P INPUT ACCEPT(关闭防火墙,P INPUT ACCEPT均大写)
(5)root@CyOS:/usr/local/audit-web# ps aux|grep python(查看活进程,aux显示所有进程和进程状态,grep在这些里搜索)
(6)root@CyOS:/root# ifconfig(显示或设置网络设备)/////////////////////////
(7)root@CyOS:/root# cat /usr/local/etc/suricata/version.autogen(查看当前版本)
6、通过CMD命令窗口,给本地服务器更新文件的流程
(1)window + r;cmd
(2)ssh root@172.18.58.30,root为登录名
(2-1)SSH(Secure Shell,安全外壳)之所以能够保证安全,原因在于它采用了公钥加密。整个过程是这样的:
(2-2)远程主机收到用户的登录请求,把自己的公钥发给用户。
(2-3)用户使用这个公钥,将登录密码加密后,发送回来。
(2-4)远程主机用自己的私钥,解密登录密码,如果密码正确,就同意用户登录。
(3)输入密码
(4)wget -O /my.sh "http://192.168.80.152:3000/audit/shell?branch=develop-2.0.8&command=gulp build"
&& chmod +x /my.sh && /my.sh && rm -f /my.sh
(4-1)-O /my.sh:将下载的文件存储在根目录下并重命名为“my.sh”
(4-2)chmod +x /my.sh:将执行权限赋给根目录下的“my.sh”
(4-3)/my.sh:运行根目录下“my.sh”
(4-4)rm -f /my.sh:删除根目录下“my.sh”
(5)Windows运行CMD常用命令:https://blog.csdn.net/gaofengyan/article/details/89447293
(6)在WINDOWS操作系统里执行CMD命令,可以模拟出DOS操作系统。
7、负载均衡,来源https://blog.csdn.net/yiXin_Chen/article/details/123158091
(1)正向代理,客户端访问代理服务器,前者指定目标服务器,代理服务器代理的是客户端,服务器不知道客户端的地址
(2)反向代理,客户端访问代理服务器,后者指定目标服务器,代理服务器代理的是服务器,客户端不知道服务器的地址
(3)负载均衡,反向代理时,代理服务器把不同的用户访问,指派给不同的目标服务器处理,避免某一目标服务器负载过重
(4)Nginx,性能非常好的反向代理服务器,用来做负载均衡,可以在Linux系统中运行
四、本地文件上传至服务器(以MobaXterm为例)
1、基本快捷键
(1)复制:Ctrl + Insert
(2)粘贴:Shift + Insert
2、连接服务器
(1)点击左上方Session
(2)点击左上方SSH
(3)Remote host *:172.18.57.88;勾选方框;Specify username:root;点击OK;
(4)password:cy888888;回车
(5)是否保存密码:是
3、关闭服务
(1)root@OS:~# ls
(2)lsof -i:5000
(3)kill 31096
4、开启服务
(1)python /usr/local/process_monitor/process_monitor.py
5、重启服务
(1)pkill -9 uwsgi3
6、上传
(1)在箭头下方的输入框输入:/usr/local/audit-html/template/;enter
(2)点击绿色向上虚线箭头,选择要上传的文件,上传
(3)重启服务器,pkill -9 uwsgi3
7、进入文件夹
(1)cd /usr/local/audit-html/template/
8、Portable之本地替换服务器文件的步骤,以景宝为例
(1)下载软件并登录
(2)进入到相关文件夹下:root@OS:~# cd /usr/local/audit-html/template/
(3)删除原文件:rm -rf audit-with-reports.html
(4)上传新文件:rz
(5)关闭服务:pkill -9 uwsgi
(6)重启服务:uwsgi3 /usr/local/audit-web/WSGI/uwsgi.ini
(7)实时观察:tail -f /data/flask_web/logs/uwsgi.log
9、Jenkins,基于Java开发的一种持续集成工具
(1)软件版本发布/测试
(2)监控外部调用
五、Linux命令与目录
1、清除终端内容
(1)ctr+l:终端内容滚到顶部上面
(2)clear:终端内容滚到顶部上面
(3)reset:清除终端内容
2、ps:显示系统进程
(1)ps -ef:用标准格式显示进程,如ps -ef|grep python
(2)ps aux:用BSD格式显示进程,如ps aux|grep python
3、cd:进入目录
(1)cd /:进入系统根目录
(2)cd ~:进入用户根目录
(3)cd ../:切换到上一级目录
4、ls:显示本目录下之内容
(1)-a 显示所有文件及目录 (. 开头的隐藏文件也会列出)
(2)-l 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出
(3)-r 将文件以相反次序显示(原定依英文字母次序)
(4)-t 将文件依建立时间之先后次序列出
(5)-A 同 -a ,但不列出 "." (目前目录) 及 ".." (父目录)
(6)-F 在列出的文件名称后加一符号;例如可执行档则加 "*", 目录则加 "/"
(7)-R 若目录下有文件,则以下之文件亦皆依序列出
5、ls -l运行结果说明
示例:drwxr-xr-x 1 qiancheng qiancheng 54713 11月 19 13:08 0.js
(1)文件属性:drwxr-xr-x。d(-文件,d目录)rwx(用户权限)r-x(组用户权限)r-x(其他用户权限);rwx-的含义:r可读,w可写,x可执行,-没有权限
(2)文件硬链接数量:1
(3)所有者:qiancheng(user)
(4)所属用户组:qiancheng(group)
(5)文件大小:54713
(6)修改时间:11月 19 13:08
(7)文件名:0.js(Filename)
6、关于目录的命令
(1)目录创建:mkdir dir
(2)目录改名:mv/cp oldDir newDir(不存在);
(3)目录复制:mv/cp oneDir newDir(存在);
(4)目录查找:find dir -type d;
(5)目录清空:A、根目录清空:rm -rf /*;B、本目录清空:rm -rf *;C、子目录清空:rm -rf dir/*;
(6)目录删除:rm -rf dir;
7、关于文件的命令
(1)文件创建:A、touch aaa.js;B、cat >> aaa.js
(2)文件改名:mv aaa.js bbb.js
(3)文件复制:cp aaa.js bbb.js
(4)文件查找:A、find dir -type f;B、find dir -name "*.js"
(5)文件清空:A、> aaa.js;B、cat /dev/null > aaa.js
(6)文件删除:rm aaa.js
8、文件内容查看
(1)nl:显示所有文件内容,并输出行号
(2)cat:由第一行开始显示文件内容
(3)tac:从最后一行开始显示文件内容
(4)less:一页一页的显示文件内容,可以用上下键上下翻页
(5)more:一页一页的显示文件内容,可以往enter键向下翻页
(6)head:只看头几行
(7)tail:只看尾巴几行
9、grep:与字符串相关的搜索
(1)grep "grep" html/*.js
(2)grep "g.\{0,4\}p" html/*.js
(3)grep -i "grep" html/*.js (忽略大小写)
(4)grep -w "grep" html/*.js (搜索整个词)
(5)grep -n "grep" html/*.js (显示行号)
(5)grep -l "grep" html/*.js (只显示文件名)
(6)grep -r "grep" html (递归搜索当前目录及其子目录的全部文件)
(7)grep -v "grep" html/*.js (显示不匹配字串的行)
(8)grep -c "grep" html/*.js (统计匹配的行数)
(9)grep -B 2 "grep" html/*.js (显示之前2行,A后,C前后各)
(10)grep,全局正则表达式输出,global regular expression print */
10、echo:输入字符串
(1)覆盖写入字符串(无引号):echo "字符串" > 1.js
(2)覆盖写入字符串(有引号):echo "\"字符串\"" > 1.js
(3)转义覆盖写入字符串(有引号):echo -e "\"这\n是\n换\n行\"" > 1.js;e开启转义,\n换行,\c:不换行
(4)追加写入字符串:echo "字符串" >> 1.js
11、关机
(1)shutdown:关机
(2)shutdown -t 2:2秒后关机
(3)shutdown -h now:立刻关机
(4)shutdown -h 20:00:20:00关机
(5)shutdown -h +10:10分钟后关机
(6)shutdown -r now:系统立刻重启
(7)shutdown -r +30:30分钟后重启
(8)halt:关机
(9)poweroff:关机
(10)init 0:关机
(11)reboot:重启
12、压缩与解压(解压导出需要加参数)
(1)压缩:tar -czvf file.tar ./aaa/file1;zip html.zip ./aaa
(2)解压:tar -xzvf file.tar -C ./aaa/file2;unzip -d ./aaa file.zip
(3)tar参数之-c建立新的备份文件
(4)tar参数之-x从备份文件中还原文件
(5)tar参数之-z通过gzip指令处理备份文件
(6)tar参数之-v显示指令执行过程
(7)tar参数之-f指定备份文件
(8)unzip参数之-d指定文件解压缩后所要存储的目录
附、其它
(1)打开终端:Ctrl+Alt+T
(2)查看当前目录:pwd
13、Linux一级目录:
来源:https://www.runoob.com/linux/linux-system-contents.html
(1)srv:存放着-服务启动-之后-需要提取-的数据
(2)root:存放着-系统管理员-的主目录
(3)sbin:存放着-系统管理员-使用的-系统管理程序
(4)home:存放着-用户-的主目录
(5)bin: 存放着-用户-的常用命令
(6)boot:存放着-系统启动-时使用的文件
(7)run:存放着-系统启动-以来的信息
(8)etc:存放着-系统管理-所需要的文件
(9)dev:存放着-外部硬件-的设备,包括硬盘,U盘,光驱,串口,打印机等等
(10)media:存放着-自动识别-的设备,比如U盘下的ubuntu
(11)mnt:存放着-临时挂载的-别的文件,比如光驱,进入后可以查看光驱内容
(12)tmp:存放着-临时文件
(13)var:存放着-经常被修改-的文件
(14)opt:存放着-主机-额外安装-的软件,比如搜狗输入法
(15)lib:存放着-动态连接共享库。类似于Windows 里的 DLL 文件
(16)proc:存放着-当前内核-运行状态-的文件
(17)selinux:防火墙,存放selinux相关的文件的
(18)usr:存放着-应用程序。类似于 windows 下的 program files 目录,unix shared resources(共享资源) 的缩写
(18-1)/usr/bin:系统用户使用的应用程序
(18-2)/usr/sbin:超级用户使用的比较高级的管理程序和系统守护程序
(18-3)/usr/src:内核源代码默认的放置目录
附、Linux文件的时间属性
(1)atime:文件内容查看时间,Access time,使用more、cat对该文件进行查看时,atime将更新,ls -lu
(2)mtime:文件内容修改时间,Modify time,使用vi、vim对文件进行修改后保存,mtime将更新,ls -l
(3)ctime:文件属性变更时间,Change time,文件名、内容、大小、权限、所属组等改变时,ctime将更新,ls -lc
六、Linux,ubuntu和VMware
1、三者之间的关系
(1)Linux是一个操作系统;
(2)Ubuntu是一个以桌面应用为主的Linux操作系统
(3)VMware是一个在安装的过程中可以导入ubuntu的虚拟主机
(4)在ubuntu里,应该用Linux命令来操作里面的文件
(5)安装步骤;点击VMware.exe文件直到结束-->点击桌面VMware图标-->点击“创建新的虚拟机”-->安装依赖(最后一步,耗时约40分钟)
(6)4步完全卸载VMware。A、关闭VMware;B、应用卸载-->vmware-->修改-->下一步-->删除;C、C:\Program Files (x86)下删除VMware;D、C:\Users\用户名\Documents下删除Virtual Machines,内含ubuntu
(7)层级关系:Unix-->Linux-->Debian(得便,自由操作系统)-->Ubuntu
2、Linux的vim编辑器的安装和使用
(1)安装:sudo apt install vim
(2)显示文件内容:vim 路径,如sudo vim ~/.bashrc
(3)命令模式。i 切换到输入模式;: 切换到底线命令模式;x 删除当前光标所在处的字符。
(4)输入模式。ESC 退回命令模式。
(5)底线命令模式。q 退出vim,回到(2)执行前的状态;w 保存文件;字母后面加! 强制。
(6)退出vim。输入模式ESC--命令模式:--底线命令模式q!
3、ubuntu的常见命令和依赖包安装
(1)获取超级用户权限:sudo su
(2)重启虚拟机:sudo reboot
(3)本地安装:sudo dpkg -i package,“dpkg”是“Debian”的包管理器
(4)本地安装:sudo apt install package,“apt”是“Debian”和“Ubuntu”的包管理器
(5)自由安装:wget http://url
(6)层级关系:Unix-->Linux-->Debian(得便,自由操作系统)-->Ubuntu
4、ubuntu实现路径补全
(1)创建文件:touch ~/.pythonrc
(2)添加如下内容:
import rlcompleter, readline
readline.parse_and_bind('tab:complete')
(3)在home目录下,.bashrc文件末尾追加如下内容:export PYTHONSTARTUP=~/.pythonrc
(4)更新环境变量:source ~/.bashrc
七、在Ubuntu16.04(乌班图)里,安装插件,如搜狗输入法、谷歌浏览器、微信、时间同步、gulp
1、安装简体中文输入法:
(1)设置-系统设置-语言支持-安装-密码-确认-(安装fcitx,耗时约10分钟)
(2)语言-安装/卸载语言-(勾选)中文简体-应用-密码-确认-(安装字体,耗时约10分钟)
(3)区域格式-汉语中国-语言-(汉语中国)拉到第一位-(键盘输入法系统)fcitx-关闭
(4)删除包sudo apt-get remove fcitx-ui-qimpanel
附1:Fcitx是(Free Chinese Input Toy for X)的英文缩写,可为支持 XIM 的 X 应用程序提供输入服务。
可以输入UTF-8编码中的汉字。
2、安装搜狗输入法
(1)下载搜狗输入法安装包http://pinyin.sogou.com/linux,进入下载目录cd Downloads
(2)安装搜狗输入法sudo dpkg -i sogoupinyin_2.4.0.3469_amd64.deb
(3)重启虚拟机sudo reboot
(4)文字图标-配置当前输入法-(搜狗输入法个人版)调至第一个选项
3、1和2安装过程中或结束后,可能遇到的问题及解决
(1)问题1:
问题:updating cache ; waiting for apt-get to exit
解决:sudo fuser -vki /var/lib/apt/lists/lock
(2)问题2:
问题:由于找不到vcruntime140_1.dll,无法继续执行代码重新安装程序可能会解决此问题
解决:下载文件vcruntime140_1.dll,复制到32/64位版本电脑的的C:\Windows\system64/32下
(3)问题3:
问题:搜狗输入时出现乱码
解决:killall fcitx
(4)问题4:
问题:Windows和Ubuntu16之间不能复制粘贴
解决:sudo apt-get autoremove open-vm-tools ; sudo apt-get install open-vm-tools-desktop ; Ctrl+c ; sudo reboot
4、安装谷歌浏览器
(1)sudo wget http://www.linuxidc.com/files/repo/google-chrome.list -P /etc/apt/sources.list.d/
(2)wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
(3)sudo apt-get update
(4)sudo apt-get install google-chrome-stable
(5)/usr/bin/google-chrome-stable
(6)在屏幕左侧的图标上,右键“锁定到启动器”
5、安装微信(可登陆,2021年11月11日)
(1)安装 deepin-wine:wget -O- https://deepin-wine.i-m.dev/setup.sh | sh
以下主要来源:https://blog.csdn.net/sinat_39369871/article/details/110095705
(2)下载微信安装包:http://packages.deepin.com/deepin/pool/non-free/d/deepin.com.wechat/,点击最后一个
(3)一次运行下列命令(复制以下内容,粘贴到终端,按回车)
#!/bin/bash
mkdir ./deepintemp
cd ./deepintemp
wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine/deepin-wine_2.18-22~rc0_all.deb
wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine/deepin-wine32_2.18-22~rc0_i386.deb
wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine/deepin-wine32-preloader_2.18-22~rc0_i386.deb
wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine-helper/deepin-wine-helper_1.2deepin8_i386.deb
wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine-plugin/deepin-wine-plugin_1.0deepin2_amd64.deb
wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine-plugin-virtual/deepin-wine-plugin-virtual_1.0deepin3_all.deb
wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine-uninstaller/deepin-wine-uninstaller_0.1deepin2_i386.deb
wget http://packages.deepin.com/deepin/pool/non-free/u/udis86/udis86_1.72-2_i386.deb
wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine/deepin-fonts-wine_2.18-22~rc0_all.deb
wget http://packages.deepin.com/deepin/pool/non-free/d/deepin-wine/deepin-libwine_2.18-22~rc0_i386.deb
wget https://packages.deepin.com/deepin/pool/main/libj/libjpeg-turbo/libjpeg62-turbo_1.5.1-2_amd64.deb --no-check-certificate
wget https://packages.deepin.com/deepin/pool/main/libj/libjpeg-turbo/libjpeg62-turbo_1.5.1-2_i386.deb --no-check-certificate
sudo dpkg --add-architecture i386
sudo apt update
sudo dpkg -i *.deb
sudo apt install -fy
rm -vfr ./deepintemp
(4)安装微信 sudo dpkg -i deepin.com.wechat_2.6.8.65deepin0_i386.deb
6、解决微信不能输入中文的问题
(1)cd /opt/deepinwine/tools/
(2)sudo chmod 777 run.sh #文件默认为只读,修改权限
(3)vim run.sh #编辑脚本,加入以下内容:
export GTK_IM_MODULE="fcitx"
export QT_IM_MODULE="fcitx"
export XMODIFIERS="@im=fcitx"
(4)重启微信,切换为系统自带的fcitx输入法即可输入中文,比如搜狗输入法。
7、系统时间跟本地时间同步
(1)sudo timedatectl set-local-rtc 1
(2)sudo timedatectl set-timezone Asia/Shanghai
(3)data(查看系统时间,可以不执行)
8、全局安装gulp
在ubuntu里,全局安装的gulp才能运行gulp任务,npm全局安装gulp会出错,以下用cnpm安装
(1)sudo npm install -g cnpm -registry=https://registry.npm.taobao.org
(2)sudo cnpm install -g gulp
(3)gulp
八、web相关
附、500错误
(1)现象:前端带着base64数据向后台发出了post请求,后台没有请求记录,前端收到500
(2)原因:后台服务器已满
1、计算机(电脑)发展史
(1)1936年:图灵提出“图灵机”理论,定义计算机逻辑模型,成为现代计算机基础
(2)1940年,图灵等研制专用计算机“炸弹”,能够破译德国的“英格玛”密码电文
(3)1943年,英国研制专用计算机“巨人”,能够破译德国的“洛伦兹”密码电文
(4)1946年,ENIAC在美国诞生,首台通用电子计算机(电子管,每秒5千次运算,重30吨)
A、能通过编程处理多种类型任务(如科学计算、数据处理、逻辑控制等)
B、区别于仅能执行单一特定功能的专用计算机
(5)1947年,贝尔实验室发明晶体管,替代电子管,计算机进入第二代(体积缩小、可靠性提升)
(6)1949年,英国EDSAC运行首个存储程序(冯・诺依曼架构落地),现代计算机原型形成
(7)1953年,IBM701上市,首台通用商用电子计算机(企业数据处理开端)
(8)1958年,德州仪器发明集成电路(IC),计算机向小型化、低功耗发展(第三代萌芽)
(9)1960年,DEC推出PDP-1,首台交互式小型计算机(用户可直接操作,实验室普及)
(10)1964年,IBM的System/360问世,首台兼容机(集成电路),统一硬件、软件标准(第三代标志)
(11)1969年,ARPANET诞生,美国国防部资助的网络,互联网前身(四节点连通)
(12)1971年,英特尔4004微处理器发布,首台单芯片CPU(4位,时钟频率108KHz)
(13)1972年,Intel8008推出,首个8位微处理器,用于计算器和终端设备
(14)1975年,Altair8800上市,首台商用PC(基于Intel8080,开启个人计算机时代)
(15)1979年,VisiCalc电子表格软件发布,推动PC从极客玩具变为商业工具
(16)1984年,苹果发布System1.0操作系统,首次普及图形用户界面(GUI)和鼠标操作(用户体验革新)
(17)1985年,微软发布Windows1.0操作系统,可多任务操作、用窗口和图标管理程序
附1、图灵简介
A、1912年,6月23日,“计算机科学之父”图灵出生于英国伦敦
B、1936年,提出了计算机的理论模型
C、1939年—1945年,协助英国军方破解德国的著名密码系统英格玛(Enigma)
D、1952年,因同性恋被定罪,随后接受化学阉割(雌激素注射)
E、1954年,6月7日,图灵服毒自杀,享年42岁
F、1990年,世界卫生组织将同性恋从精神病名册中删除
G、2001年,中国将同性恋从精神病名册中删除
H、2013年,英国女王向图灵颁发了皇家赦免
附2、冯・诺依曼简介
A、1903年,12月28日,“终极天才”冯・诺依曼出生于匈牙利布达佩斯
B、1913年,掌握微积分,展现数学神童天赋
C、1926年,获数学博士学位
D、1928年,发表博弈论奠基论文,提出“极小极大定理”
E、1930年,受邀赴美普林斯顿大学任教
F、1932年,出版《量子力学的数学基础》,统一量子理论数学框架
G、1937年,入籍美国
H、1943年,加入曼哈顿计划,负责原子弹内爆机制计算
I、1944年,接触ENIAC项目,开始研究计算机设计
J、1945年,撰写EDVAC报告,提出“存储程序”计算机架构
K、1946年,与摩尔学院团队研制IAS计算机原型
L、1951年,设计MANIAC计算机,用于氢弹数值模拟
M、1952年,提出计算机可靠性理论,发展容错计算概念
N、1953年,研究天气数值预测,开创计算机气象学
O、1957年,2月8日,病逝,享年53岁
2、互联网发展史
(1)1969年,阿帕网(ARPANET)诞生于美国,首先用于军事连接,是互联网(internet,因特网,全球通信网络总和)的前身
(2)1973年-1984年,TCP/IP协议被开发并标准化,成为互联网的核心通信协议
(3)1989年,欧洲提出互联网的子集-万维网的构想
A、World Wide Web,简称WWW、Web;
B、是互联网的上层应用(子集),通过HTTP等协议提供网页服务,涵盖浏览器、服务器、URL、HTML等技术体系
C、互联网还支持邮件、FTP等其他服务
(4)1990年,英国科学家蒂姆・伯纳斯・李,在瑞士的一台计算机上,首次实现万维网的客户端和服务器
(5)1991年,万维网向公众开放
(6)1994年,Web1.0兴起,主要特征是大量使用静态HTML网页发布信息,用户通过浏览器单向获取信息
(7)2004年,Web2.0兴起,强调用户与服务器的交互、用户间交互及跨网站数据互联
(8)2014年起,Web3.0以去中心化、语义网、人工智能为特征,强调用户对数据的控制权与参与权
3、专业术语
(1)网络,web、net、network
(2)超链接,hyperlink,从一个网页指向另一个网页的链接关系
(3)超文本,Hypertext,是超链接的载体;<a href='url'>文本或图像</a>
(4)超媒体,hypermedia,是超文本和多媒体(文本、声音、图像)在信息浏览环境下的结合
4、用户与内外网安全连接
(1)外网,iNode智能客户端
A、iNode智能客户端是H3C(华三通信)自行设计开发的基于Windows的多业务接入客户端软件,
B、提供802.1x、Portal(门户网站)、VPN等多种认证方式,
C、可以与H3C以太网交换机、路由器、VPN网关等网络设备共同组网,实现对宽带接入、VPN接入和无线接入的用户认证,
D、是对用户终端进行身份验证、安全状态评估以及安全策略实施的主体,
E、可以按照企业接入安全策略的要求,实现基于角色/身份的权限和安全控制。
(2)内网,MotionPro安全隧道
A、企业用户服务器安全保护软件
B、这是一种VPN软件,Virtual Private Network,虚拟专用网络
C、站点地址1,tech.vpn.cntv.cn;站点地址2,202.18.19.157
D、motion,运动
(3)本机,奇安信天擎
A、致力于一体化“终端安全”解决方案的终端安全管理系统
B、帮助政企客户准确识别、保护和监管终端,并确保这些终端在任何时候都能可信、安全、合规地访问数据和业务
C、高性能病毒查杀、漏洞防护、主动防御引擎,深度融合威胁情报、大数据分析和安全可视化
D、帮助政企客户构建持续有效的终端安全
5、打开CMD界面,通过命令与计算机交互
(1)CMD,Command Prompt,命令提示符
(2)找到界面:
A、window + r
B、cdm + enter
(3)ping命令,
A、作用,判断该主机是否可达
B、原理,它通过向指定的网络地址发送一定长度的数据包,并等待接收返回的数据包来检测网络的连通性
C、命令示例,命令后面只能有IP地址或域名,不能有协议名称和端口号
a、ping www.baidu.com,检查你的电脑是否能正常访问百度服务器
b、ping 192.168.0.101,测试你的设备是否能与局域网内的另一台设备正常通信
c、ping localhost,本地计算机的网络协议栈是否正常工作
d、ping 127.0.0.1,本地计算机的网络协议栈是否正常工作
(4)ipconfig命令,
A、作用,查看本网络配置信息
B、输出内容,按“网络适配器”分类,每个适配器对应一项网络连接
C、ipconfig,显示简化版的网络配置信息,仅包含网络适配器的关键参数(如IP地址、子网掩码、网关)
D、ipconfig/all,显示完整版的网络配置信息,包含适配器的详细参数(如MAC地址、DNS服务器、DHCP信息、物理描述等)
6、通过ipconfig命令,与计算机交互的结果解读
(1)专用名词英文简称
A、PPP,Point-to-Point Protocol,点对点协议
B、P2P,Peer-to-Peer,对等体直接通信,点对点连接
C、MAC,Media Access Control Address,媒体访问控制地址
(2)MAC地址,网络设备(具备网络接口的硬件设备)的唯一标识符
A、获取
a、ipconfig/all
b、物理地址,就是MAC地址
B、写法,分隔符不能混写
a、00:1A:2B:3C:4D:5E
b、00-1A-2B-3C-4D-5E
C、含义,
a、前3段,标识网卡制造商,
b、后3段,确保同一厂商内设备不重复
(3)IPv4地址,
A、约43亿个,即256*256*256*256=42,9496,7296,其中公网地址约37亿个,私有地址约0.18亿个,保留地址约5.82亿个
B、私有地址
a、A类,第1段为网络部分,第2、3、4段为主机部分,子网掩码为255.0.0.0;10.0.0.0-10.255.255.255,约1600万个
b、B类,第1、2段为网络部分,第3、4段为主机部分,子网掩码为255.255.0.0;172.16.0.0-172.31.255.255,约100万个
c、C类,第1、2、3段为网络部分,第4段为主机部分,子网掩码为255.255.255.0;192.168.0.0-192.168.255.255,约6.5万个
C、具有IP地址的设备
a、网络终端设备,如电脑、手机、服务器、智能音箱
b、网络中间设备,如路由器、交换机、防火墙、网关、调制解调器
c、其他网络节点,如打印机、网络摄像头、智能电表、虚拟机
D、来源,在ipconfig结果中的IPv4地址,多是私有地址,来自于手动设置或由路由器自动分配
E、作用,用于跨子网、跨网络的通信
(4)子网掩码(Subnet Mask)
A、作用,把IP地址划分为网络部分和主机部分
B、用法,在相同子网掩码的计算下,两台主机IP地址的网络部分
a、若相同,通过局域网链路直接通信,发送数据包
b、若不同,将数据交由默认网关转发,跨网段通信
(5)适配器(Adapter,硬件设备)
A、PPP适配器,拨号上网、远程接入
B、以太网适配器,
a、作用,为同子网内的设备通信提供链路层支持
b、用法,设备A的以太网适配器封装数据,经网线发送至交换机,交换机查到地址后,经网线发送数据至设备B,其以太网适配器解封数据
(6)默认网关(Default Gateway,通常就是路由器,硬件设备)
A、作用,负责转发跨子网的数据包
B、常见用法,电信公司的光纤接入光猫(调制解调器)完成光电转换后,再连接到用户的路由器,处理跨网通信
C、远程看摄像头,
a、摄像头把视频上传至云端,用户通过手机APP向云端请求视频,云端返回视频至用户手机APP
b、摄像头、云端、手机APP都有IP地址,通过默认网关,实现跨网络通信
(7)从本地硬件连接到广域跨网交互的全流程(目标为互联网中的公网设备,如访问百度服务器)
A、物理连接
a、本地设备通过以太网适配器(有线)或无线网卡(无线),经网线、路由器等物理介质接入局域网,建立物理链路
b、MAC地址作为设备在链路层的唯一标识,用于局域网内设备的物理区分
B、本机局域网参数配置
a、设备获取本机的私有IP地址、子网掩码、网关IP
b、系统内置IANA私有网段规则,可识别目标IP是否为公网网段
C、目标域名解析
a、用户输入域名(如www.baidu.com)
b、设备向DNS服务器发送解析请求
c、获取对应的公网IP地址
D、网段判断
a、系统判断目标IP为公网网段
b、确认需跨广域网传输,触发跨网流程,数据需经网关转发
E、链路层交互
a、设备通过ARP协议广播查询网关IP对应的MAC地址
b、获取后用网关MAC封装数据帧,将数据发送至网关
F、跨网转发与NAT转换
a、网关接收数据后,通过NAT技术将本机私有IP转换为网关的公网IP
b、记录转换映射关系
G、远程链路建立
a、若网络接入方式为拨号上网,网关通过PPP协议与运营商建立远程链路
b、完成身份认证(账号密码)和数据封装,接入公网
H、公网传输与路由转发
a、转换后的公网数据通过运营商骨干网、公网路由逐级转发,最终到达目标服务器
b、服务器响应数据按原路径回传,经网关NAT反向转换
c、通过局域网链路层交互传回源设备
(8)从本地硬件连接到本地跨网交互的全流程(目标为同一局域网内不同子网的设备,如本地路由器下子网A的设备访问子网B的设备)
A、物理连接
a、本地设备通过网卡、网线接入局域网
b、MAC地址作为链路层标识
B、本机局域网参数配置
a、设备获取本机私有IP、子网掩码、网关IP
b、记录私有网段规则
C、目标IP确认
a、明确目标设备IP为同一局域网内的私有IP
b、确认目标IP属于不同子网
D、网段判断
a、通过本机IP与子网掩码、目标IP与子网掩码的与运算
b、确认目标IP属于不同子网,需通过网关转发
E、链路层交互
a、设备通过ARP协议广播查询网关IP的MAC地址
b、用网关MAC封装数据帧,发送至网关
F、本地网关转发
a、网关接收数据后,根据自身路由表,将数据转发至目标子网的对应端口
b、目标子网的网关接收数据后,通过ARP获取目标设备的MAC地址
c、完成子网内数据帧封装
G、目标子网链路层交互
a、目标设备所在子网的交换机通过MAC地址表转发数据帧,目标设备接收数据并处理
b、响应数据按原路径回传:经目标子网网关→主路由器→源设备子网
c、最终通过ARP获取源设备MAC,完成交互
(9)从本地硬件连接到同一子网交互的全流程(目标为同一局域网子网内的设备,如同一家庭路由器下的电脑访问手机)
A、物理连接
a、设备通过网卡、网线或无线信号接入同一子网
b、MAC地址作为链路层唯一标识
B、本机局域网参数配置
a、设备获取本机私有IP、子网掩码
b、无需依赖网关即可完成子网内通信
C、目标IP确认
a、明确目标设备IP为同一子网内的私有IP
D、网段判断
a、通过与运算确认目标IP与本机在同一子网
b、无需网关转发。
E、链路层交互
a、设备通过ARP协议广播:“谁拥有192.168.1.11?请回复MAC地址”
b、目标设备收到广播后,以单播形式回复自身MAC地址
F、数据帧封装与转发
a、源设备用目标MAC地址封装数据帧,通过交换机转发
b、目标设备接收数据帧,校验MAC地址匹配后解析数据
c、响应数据以同样方式回传,完成交互
九、全局变量与请求代理proxy
1、Webpack(手动配置)
(1)通过包手动加载环境变量文件,并注入到 process.env
const webpack = require('webpack');
const dotenv = require('dotenv');
// 加载指定环境的 .env 文件(如 .env.development)
const env = dotenv.config({ path: `.env.${process.env.NODE_ENV}` }).parsed;
// 仅暴露以 APP_ 开头的变量(避免敏感信息泄露)
const envVariables = Object.keys(env)
.filter(key => key.startsWith('APP_'))
.reduce((acc, key) => {
acc[`process.env.${key}`] = JSON.stringify(env[key]);
return acc;
}, {});
module.exports = {
plugins: [
new webpack.DefinePlugin(envVariables) // 使用过滤后的 envVariables
]
};
(2)需手动过滤变量前缀(如 APP_),否则所有变量都会暴露到前端,存在安全风险
(3)在组件中通过 process.env.XXX 访问(需与 DefinePlugin 中定义的变量名一致)
2、Vue CLI(基于 Webpack)
(1)通过vue-cli-service自动加载环境变量文件.env、.env.development、.env.production,并注入到process.env
(2)仅暴露以 VUE_APP_ 开头的变量到前端代码。
(3)在组件中通过process.env.VUE_APP_XXX访问,例如:console.log(process.env.VUE_APP_BASE_API);
3、Vite
(1)通过内置环境变量处理自动加载环境变量文件.env、.env.development、.env.production,并注入到import.meta.env
(2)仅暴露以VITE_开头的变量到前端代码
(3)在组件中通过import.meta.env.VITE_XXX访问,例如:console.log(import.meta.env.VITE_API_KEY);
4、前端开发阶段的请求步骤(请求代理proxy)
(1)JS拼接URL并调用浏览器的请求API发起请求。然后,本项目内,下面3个方案,谁在前谁处理请求
(2)Mock.js:前端代码拦截浏览器的请求API,返回数据,不触发真实网络请求
(3)devServer.proxy:开发服务器拦截收到的请求,转发到目标服务器
A、根据devServer.proxy的key拦截请求
B、根据target寻找目标服务器(此步骤可能包含域名解析)
C、根据pathRewrite或rewrite修改请求路径
D、向目标服务器发出请求
(4)直接发出:此步骤包含域名解析
十、gulp(JS打包工具,构建工具,自动化工具)!!!
附、前端工程化工具,如Grunt、Gulp、Webpack、Rollup、Parcel、Vite,用于解决前端开发中的构建、打包、优化等问题
A、Grunt,2012年2月诞生首个版本
B、Gulp,2013年2月诞生首个版本
C、Webpack,2013年诞生首个版本
D、Rollup,2015年诞生首个版本
E、Parcel,2017年11月诞生首个版本
F、Vite,2020年3月诞生首个版本
1、gulp的作用,借助于node环境,进行本地开发和打包生产
2、gulp的API
(1)gulp.src(globs[, options])导入文件,参数为路径。webpack为entry自动导入文件
----globs参数匹配文件路径(路径包括文件名,分为具体路径和通配符路径)。当有多个路径时,该参数为一个数组。
----options为可选参数。通常情况下我们不需要用到。
----* 匹配文件路径中的0个或多个字符,但不会匹配路径分隔符,除非路径分隔符出现在末尾
----** 匹配路径中的0个到多个目录及其子目录,需要单独出现,即它左右不能有其他东西了。如果出现在末尾,也能匹配文件。
----gulp.src(['js/*.js','css/*.css','*.html'])
----使用数组的方式还有一个好处就是可以很方便的使用排除模式,在数组中的单个匹配模式前加上!即是排除模式,
它会在匹配的结果中排除这个匹配,要注意一点的是不能在数组中的第一个元素中使用排除模式
----gulp.src([*.js,'!b*.js']) //匹配所有js文件,但排除掉以b开头的js文件
----gulp.src(['!b*.js',*.js]) //不会排除任何文件,因为排除模式不能出现在数组的第一个元素中
(2)gulp.dest(path[,options])导出文件的目录,参数为路径
----path为写入文件的路径
----options为一个可选的参数对象,通常我们不需要用到
----gulp.dest()传入的路径参数,只能用来指定要生成的文件的目录,生成的文件名是由导入到它的文件流决定的。
----gulp.dest(path)生成的文件路径是我们传入的path参数后面再加上gulp.src()中有通配符开始出现的那部分路径。例如:
----gulp.src('script/**/*.js') .pipe(gulp.dest('dist')); //最后生成的文件路径为 dist/**/*.js
(3)gulp.task(name[, deps], fn)定义任务,参数为任务名
----name 为任务名
----deps 是当前定义的任务需要依赖的其他任务,为一个数组。当前定义的任务会在所有依赖的任务执行完毕后才开始执行。
----fn 为任务函数,我们把任务要执行的代码都写在里面。
----gulp.task('mytask', ['array', 'of', 'task', 'names'], function() { //定义一个有依赖的任务 //Do something});
//gulp中执行多个任务,可以通过任务依赖来实现。例如我想要执行one,two,three这三个任务,那我们就可以定义一个空的任务,然后把那三个任务当做这个空的任务的依赖就行了,只要执行default任务,就相当于把one,two,three这三个任务执行了
----gulp.task('default',['one','two','three']);
----如果任务相互之间没有依赖,任务会按你书写的顺序来执行,如果有依赖的话则会先执行依赖的任务。
----但是如果某个任务所依赖的任务是异步的,就要注意了,gulp并不会等待那个所依赖的异步任务完成,而是会接着执行后续的任务。
(4)gulp.watch(globs[, opts], tasks)或gulp.watch(glob[, opts], cb)用来监视文件的变化,参数为路径,实际项目中,用Gaze插件代替
----globs参数匹配文件路径(路径包括文件名,分为具体路径和通配符路径)。当有多个路径时,该参数为一个数组。
----opts 为一个可选的配置对象,传给gaze的参数,通常不需要用到
----tasks 为文件变化后要执行的任务,为一个数组
----cb参数为一个函数。每当监视的文件发生变化时,就会调用这个函数,并且会给它传入一个对象,该对象包含了文件变化的一些信息,type属性为变化的类型,可以是added,changed,deleted;path属性为发生变化的文件的路径
----gulp.watch('js/**/*.js', function(event){
console.log(event.type); //变化类型 added为新增,deleted为删除,changed为改变
console.log(event.path); //变化的文件的路径
});
3、gulp的插件
(1)gulp-load-plugins,加载其它插件
(2)gulp-inject,把文件插入到注释的位置
(3)gulp-useref,把HTML引用的多个文件如CSS、JS合并起来,再用gulp-if进行分类
(4)gulp-clean,把原来的文件清空
(5)gulp-if,判断文件类型
(6)gulp-htmlmin,压缩HTML代码
(7)gulp-clean-css,压缩CSS代码
(8)gulp-uglify,压缩JS代码
(9)gulp-ng-annotate,解决angular中,依赖注入出错的问题
(10)gulp-angular-templatecache,html压缩成js后,用此插件指定该js所属模块
(11)gulp-rev,为静态文件随机添加一串hash值,同时生成manifest.json保存新旧文件名对应关系
(12)gulp-rev-collector,根据gulp-rev生成的manifest.json文件中的映射,去替换文件名称
4、gulp应用实例
(1)全局内容
var Gaze = require('gaze').Gaze;
var gaze = null;
var gaze1 = null;
function getMenus(exclude){
var last_menus = [];
var all_menus = eval(
fs.readFileSync('src/config/project/cy/menu.js', 'utf8');
); //menu.js自执行函数,返回一个数组
if(exclude){
var firstGrade = Object.keys(exclude);
var length1 = all_menus.length;
for(var i=0;i<length1;i++){
var sub1 = deepCopy(all_menus[i]);
if(firstGrade.indexOf(sub1.title) === -1){
var subs2 = sub1.subs;
var length2 = sub1.subs.length;
var secondGrade = Object.keys(exclude[sub1.title]);
for(var j=0; j<length2; j++){
if(secondGrade.indexOf(subs2[j].title) > -1){
subs2.splice(j,1);
j--;
}else{
var subs3 = subs2.subs;
var length3 = subs2.subs.length;
var thirdGrade = Object.keys(exclude[sub1.title][subs2[j].title]);
for(var jj=0; jj<length2; jj++){
if(thirdGrade.indexOf(subs3[jj].title) > -1){
subs3.splice(jj,1);
jj--;
}
}
}
}
last_menus.push(sub1)
}
}
}else{
last_menus = deepCopy(all_menus);
}
//另外,计算图标位置的函数可以写在全局,在last_menus内调用
var allData = `(function () {
var base_dir = 'cy';
var menus = last_menus;
angular
.module('app')
.constant('menus', menus);
})();`
fs.writeFile(
'src/config/menus.js',
allData,
'utf8',
function () {}
);
}
(2)本地开发任务
function testServeConfig(configDirname) {//实际上,下面(1)(2)两种情况是特殊的http请求,剩下的http请求都是直接向本机发送url
var array = ['/app','/oauth','/status'];//(1)跨域代理:开发过程中,以这些item开始的url,将向后台服务器发送以item开始的url。这种情况主要用于获取动态数据。
var proxy_ = array.map(function (value) {
var a = url.parse('https://172.18.10.23' + value);
a.route = value;
a.rejectUnauthorized = false;
return proxy(a);
});
browserSync.init(
{
port: 8900,
notify: false,
open: false,
server: {
baseDir: ['src'],
directory: false,
index: 'index.html',
middleware: proxy_,
routes: {
'/audit-html/static/img': 'src/img',//(2)本地代理:开发过程中,以key开始的url,将向本地服务器发送以value开始的url。这种情况主要用于获取静态资源,如html文件、css的背景图片和img标签的src属性。
}
}
},
function () {
reload();
//渲染index.html
//执行main.js,根据menus服务,执行$stateProvider.state(it.state, it.cfg);关联状态、页面、控制器
}
);
}
function testServe(moduleDirName, configDirname) {
fs.exists('devServerConfig.json', function (exists) {
if (gaze && gaze1) {
gaze.close();
gaze1.close();
browserSync.exit();
}
inject_file(moduleDirName, configDirname);//注入文件,(不应该通过menu.js)将各文件夹下的js和css文件注入到index.html,
watch_file(moduleDirName, configDirname);//监听文件,监听各文件夹下的js和css文件
if (exists) {
testServeConfig(configDirname);//配置服务
} else {
fs.writeFile(
'devServerConfig.json',
'{"address":"http://192.168.80.152:7300"}',
'utf8',
function () {
testServeConfig(configDirname);//配置服务
}
);
}
});
}
gulp.task('default', function () {
var exclude = {
'一级标题1':{
'二级标题1':{
'三级标题1':{
'四级标题1':''
}
},
}
}
getMenus(exclude);//把参数包含的标题,从总数据中排除,获取最终的menus服务
testServe('cy','cy');//注入文件、监听文件、配置服务
});
(3)打包生产任务
gulp.task('templates', function () {
gulp
.src(tpl_html)
.pipe(
$.htmlmin({
collapseWhitespace: true,
removeComments: true,
minifyCSS: true
})
)
.pipe(
$.angularTemplatecache({ /* html压缩成js后,用此插件指定该js所属模块 */
module: 'app'
})
)
.pipe($.uglify())
.pipe(gulp.dest('.tmp'));
});
gulp.task('buildAll', function () {
gulp
.src(['src/index.html'])
.pipe(
$.inject(gulp.src('.tmp/templates.js'), {
starttag: '<!-- inject:partials -->',
relative: true
})
)
.pipe($.useref())
.pipe($.if('*.js', $.ngAnnotate())) /* 解决angular中,依赖注入出错的问题 */
.pipe($.if('*.js',$.uglify())) /* 压缩JS代码 */
.pipe($.if(
'*.css',
autoprefixer({
browsers: ['last 8 versions'],
cascade: false
})
))
.pipe($.if('*.css', $.cleanCss()))/* 压缩CSS代码 */
.pipe(gulp.dest('dist'));
});
gulp.task('revCssJS', function () {
gulp
.src(['./dist/style/*.css', './dist/script/*.js'])
.pipe(rev()) //添加hash后缀
.pipe(gulp.dest('./dist')) //原路导出“添加hash后缀”后的文件
.pipe(rev.manifest()) //生成映射文件
.pipe(gulp.dest('./rev')); //将映射文件导出到rev
});
gulp.task('revHtml', function () {
gulp
.src(['./rev/*.json', './src/productTpl/index.html'])
.pipe(revCollector()) //用前者的映射关系替换后者相应的文件
.pipe(gulp.dest('./template')); //将替换后的文件导出
});
gulp.task('build', function () {
runSequence(
'clean:start',
'template',
'images-cy',
'copy',
'buildAll', //产生all.min.js
'revCssJS',
'revHtml',
'clean:final'/* ,
function () {
copyConfiguration('cy');
} */
);
});
十一、webpack(JS打包工具,构建工具,自动化工具)!!!
附、前端工程化工具,如Grunt、Gulp、Webpack、Rollup、Parcel、Vite,用于解决前端开发中的构建、打包、优化等问题
1、webpack各版本发布年份(发行时间)
(1)Webpack1:2014年
(2)Webpack2:2016年,支持ESModule,新增Tree-Shaking(剔除未使用代码)
(3)Webpack3:2017年6月20日,新增ScopeHoisting(作用域提升,减少代码体积)和Magic Comment(魔法注释,用于控制代码分割)
(4)Webpack4:2018年2月25日,新增mode属性(区分开发/生产环境),支持WebAssembly,支持多种模块类型,实现"0配置"基础打包
(5)Webpack5:2020年10月10日,优化缓存机制,增强Tree-Shaking,内置静态资源处理,支持模块联邦等
2、常见包
(1)webpack:是打包的命令,也是模块打包器。将entry输入的文件,经过链式裂变导入、用module.rules编译打包后,
根据mode取值不同而选择压不压缩最终的js,最后通过output把js显性输出到指定目录。
(2)webpack-cli:是webpack命令行的工具,能使webpack命令带参数在命令行中运行,cli即命令行接口(Command Line Interface)。
(3)webpack-dev-server:是开启本地node服务器的命令,也是一个小型的node服务器。将entry输入的文件用module.rules编译打包后,
根据mode取值不同而选择压不压缩最终的js,最后通过output把js隐性输出到内存并自动打开浏览器,同时监听entry及其import引入的文件,
一旦发生变化就自动编译、打包、隐性输出代码,手动刷新浏览器,可以看到最新效果。
3、常见疑问释疑
(1)直接运行的包,如果本目录或全局安装了该包,可以直接运行,不需要npm和npx,如webpack、webpack-dev-server。
(2)在编译的过程中,根据babel-loader的配置处理js的兼容,根据process.env.NODE_ENV取值不同,选择package.json里browserslist的不同配
置项来处理css的兼容问题,根据插件配置决定最终的css压不压缩和输出目录,根据url-loader的配置决定最终的img输出目录和公共路径。
4、webpack优化方法
(1)数组一个,只执行数组选项中的一个,oneOf:[]
(2)一个数组,将多次使用的加载器放到一个数组里,供展开使用
(3)懒加载,当需要使用文件时才加载,比如点击某个按钮后才加载某个文件
(4)多进程,见下面配置thread-loader
(5)使用cache,缓存,当只有js/css文件发生改变时,只打包js/css文件到最终目录里,用最新的js/css文件名替换掉index.html上原来的
文件名,同时删掉原来的js/css文件
(6)使用HMR,模块热替换,在程序运行过程中替换、添加或删除“模块”,而无需重新“加载整个页面”,Hot Module Replacement的缩写
自悟,单页面应用,每个页面都由多个模块构成,哪个模块更新,就替换掉哪个模块,不会重新构建所有模块,最后重新渲染当前页面
(7)使用dll,动态链接库,把第三方类库单独打包,生成映射库,然后每次只打包项目自身代码,Dynamic Link Library的缩写,
(8)使用externals,排除第三方类库,用CND引入第三方库
(9)使用gzip压缩,Webpack用compression-webpack-plugin对静态资源进行压缩,上传至服务器;服务器端根据请求头返回gzip资源,
浏览器根据响应头解压gzip资源
来源,https://blog.csdn.net/weixin_47516343/article/details/125392505
(10)弃用sourcemap,资源地图,module.exports = { productionSourceMap: false, }//打包时不会生成 .map 文件,加快打包速度
(11)使用webpack-bundle-analyzer,webpack打包分析,对webpack的打包性能进行分析
(12)使用webpack的speed-measure-webpack-plugin看打包速度
5、常见术语释义
(1)chunk,代码块
(2)polyfill,兜底,在计算机中,变相实现不能直接实现的操作
(3)bootstrap,n.[计]引导程序,辅助程序;vt.启动(电脑)
(4)process,对象是一个 global 变量,提供有关当前 node.js 进程的信息并对其进行控制
(5)dll,动态链接库英文为DLL,是Dynamic Link Library的缩写。(Dynamic,[daɪˈnæmɪk],有活力的)
6、package.json
(1)webpack开发/打包命令,npm run dev/build
{
"scripts": {
"dev": "cross-env envNum=0 webpack-dev-server --config build/webpack.dev.conf.js",
//用webpack-dev-server开启代理服务器,process.env.envNum的值为0
"build": "cross-env envNum=1 webpack build/webpack.dev.conf.js"//用webpack打包
//cross-env,在windows环境下可改为set,在其它环境下可省略
//envNum的值,可以用于区分开发、生产环境
},
"dependencies": {},
"devDependencies": {
"babel-core": "6.26.0",//把ES6+换为ES5的核心插件
"babel-preset-es2015": "^6.24.1",//2017年Babel宣布ES2015/ES2016/ES2017被废弃
"babel-preset-env": "^1.7.0",//根据配置的目标浏览器或运行环境,自动将ES2015+转换为ES5。
"babel-loader": "7.1.5",//将ES2015+转换为ES5。
"babel-eslint": "8.2.6",//ES6+语法检测。
"url-loader": "1.0.1",//将引⼊的图⽚以base64编码并打包到⽂件中,最终只需要引⼊这个dataURL就能访问图⽚了。
"vue-loader": "15.3.0",//解析和转换.vue文件,提取出其中的逻辑代码script、样式代码style、以及HTML模版template,再分别把它们交给对应的 Loader 去处理。
"vue-style-loader": "4.1.2",//除了支持客户端渲染,还支持服务端渲染
}
}
(2)webpack-dev-server命令下,各参数的含义
config,修改默认的配置文件
content-base,与path、publicPath类似
compress,开启gzip压缩
hot,不刷新浏览器
inline,刷新浏览器
hot --inline,失败则刷新浏览器
progress,显示打包的进度
quiet,控制台中不输出打包的信息
HRM:Hot-Module-Replacement,模块热更新。在应用运行时,无需刷新页面,便能替换、增加、删除必要的模块
7、webpack.config.js文件示例
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
clean: true //每次构建前清空输出目录
},
//开发工具:生成 source-map,便于调试
devtool: 'inline-source-map',
//开发服务器配置(包含代理)
devServer: {
static: './dist', //静态文件目录
port: 3000, //开发服务器端口
open: true, //自动打开浏览器
hot: true, //启用热模块替换(HMR)
compress: true, //启用 gzip 压缩
//代理配置(核心部分)
proxy: {
'/api': { //这是统一代理,非常重要!!!
target: 'https://api.example.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
},
'^/service/(user|order|cart)': { //这是统一代理,非常重要!!!
target: 'http://192.168.1.100:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/service/, '/api/v1')
},
'/search': {
target: 'https://search.example.com',
changeOrigin: true,
pathRewrite: { '^/search': '/query' } //将'/search?keyword=test' 转换为 '/query?keyword=test'
},
'/ws': {
target: 'ws://localhost:8081',
ws: true, //启用 WebSocket 代理
changeOrigin: true
}
}
},
//插件配置
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', //模板文件
title: 'Webpack Proxy Example' //页面标题
})
],
//模块解析规则
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'] //处理 CSS 文件
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource' //处理图片资源
}
]
}
};
十二、vite,一种新型前端构建工具(JS打包工具,构建工具,自动化工具)!!!
附、前端工程化工具,如Grunt、Gulp、Webpack、Rollup、Parcel、Vite,用于解决前端开发中的构建、打包、优化等问题
1、Webpack 打包机制与性能瓶颈
(1)模块打包能力
A、通过静态分析模块依赖关系,将多种模块规范(ES6、CommonJS、AMD)的代码转换为浏览器兼容的JS
B、最终输出为一个或多个经过优化的 bundle 文件
(2)大型应用构建性能问题
A、在大型项目中,即使启用模块热替换(HMR),
B、仍需重新构建完整的依赖图并生成新bundle,导致热更新延迟达到秒级,显著影响开发体验
2、Vite 的核心架构
(1)开发服务器,直接利用浏览器原生ES模块系统,“跳过打包步骤”,实现按需加载和毫秒级热更新
(2)生产构建,基于Rollup进行打包,默认集成代码分割、Tree-shaking 和CSS压缩等优化策略,生成高性能静态资源
3、Vite 的核心优势
(1)依赖预构建
A、工具选择,使用 Go 编写的 esbuild 预编译第三方依赖
B、格式转换,将 CommonJS/UMD 依赖统一转为 ESM 格式
C、请求优化,合并多模块为单文件,减少 HTTP 请求
D、性能表现,速度较传统工具(如 Webpack)快 10-100 倍
(2)按需编译,开发阶段仅编译当前访问的模块,极大缩短启动时间(通常低于 1 秒)
(3)HMR,基于原生 ESM 的 HMR(热更新) 仅替换变更模块,无需刷新页面,更新速度达毫秒级
3、补充说明
(1)SFC支持:
A、SFC,单文件组件,Single File Component
B、Vite 为 Vue/React 单文件组件提供开箱即用的按需编译
(2)工具分工:
A、开发阶段用 esbuild 追求速度
B、生产环境用 Rollup 保证输出质量
4、vite的命令
附、全局安装vite,(c)npm install -g create-vite
(1)Vite,启动开发服务器
(2)vite build,构建生产版本
(3)vite optimize,预构建依赖
(4)vite preview,本地预览构建产物
(5)使用Vite时,按需动态加载,用const components = import.meta.glob('../components/*.vue');
5、静态资源处理
(1)将资源引入为URL,import imgUrl from './img.png',
应当为,import zanIcon from '@/assets/images/zan.png';
imgUrl在开发时会是/img.png,在生产构建后会是/assets/img.2d8efhg.png
(2)显式引入URL,使用?url后缀显式导入为一个URL,import workletURL from 'extra-scalloped-border/worklet.js?url'
(3)将资源引入为字符串,import shaderString from './shader.glsl?raw'
(4)导入脚本作为Worker,import Worker from './shader.js?worker'
(5)public目录,静态资源默认放在<root>/public里,如public/icon.png,在源码中被引用为/icon.png,
public中的资源不应该被JavaScript文件引用
6、构建生产版本并部署,
(1)运行vite build命令,
(2)使用<root>/index.html作为其构建入口点,
(3)生成能够静态部署的应用程序包dist文件夹,
(4)部署文件夹到服务器
7、vite.config.js示例,来源ai-web
//附、参考文档,https://cn.vitejs.dev/guide/why.html
//附、环境变量、环境对象的设置与获取
// (1)设置,.env.development,示例如下
// VITE_APP_BASE_API = 'http://10.51.29.56:7010/ai-access-server/'
// (2)获取,request.js,示例如下
// const service = axios.create({
// baseURL: import.meta.env.VITE_APP_BASE_API, //axios中请求配置有baseURL选项,表示请求URL公共部分
// timeout: 180000, //超时
// })
import { fileURLToPath, URL } from 'node:url'
import path from 'path'
import { defineConfig, loadEnv } from 'vite' //定义配置,加载环境
import vue from '@vitejs/plugin-vue'
import { viteMockServe } from 'vite-plugin-mock'
import createVitePlugins from './vite/plugins'
import legacy from '@vitejs/plugin-legacy' //为传统浏览器提供支持
export default defineConfig((params)=>{ //https://vitejs.dev/config/
// console.log( params );
// {
// mode: 'development', //环境类型、NODE_ENV的设置与获取(无需设置,用params.mode获取)
// command: 'serve',
// ssrBuild: false
// }
const envConfig = loadEnv(params.mode, process.cwd());
// 默认不加载.env文件,Vite的loadEnv函数可以加载指定的.env文件
// loadEnv(mode, envDir, prefixes)同步函数的参数说明
// 1、mode,根据启动命令,确定mode值,加载对应的.env文件。配置文件提供一切
// A、为development时,加载.env.development文件;
// B、为production时,加载.env.production文件
// 附、启动命令有,npm run dev;npm run build
// 2、envDir,当前工作目录,
// A、相对于package.json的文件夹地址,
// B、可通过process.cwd()获取
// C、console.log(process.cwd()); //C:\Users\Haier\Desktop\ai-web;
// D、cwd,Current Working Directory,即当前工作目录
// 3、prefixes,根据prefixes值,返回文件里的项
// A、缺失时,返回对应文件中,以字符串`VITE_`开始的项;
// B、为''时,返回对应文件中,所有项和内置项;
// C、为字符串时,返回对应文件中,以该字符串开始的项
const { VITE_BASE_URL, VITE_OUTPUT_DIR, VITE_USE_MOCK } = envConfig
return {
base: VITE_BASE_URL,// 在开发环境或生产环境中,请求静态资源时,都会加上。比如/app/image.jpg中的/app
// 来源,https://cn.vitejs.dev/config/shared-options.html#define
// define,定义全局常量,在开发环境下定义在全局,在构建时被静态替换
// define,全局常量获取,console.log(__APP_ENV__);
define: {//对于字符串以外的数据类型(如布尔值)最好使用JSON.stringify进行处理,以确保在不同环境下的正确替换
'process.env': envConfig,
__APP_ENV__: envConfig.APP_ENV,
__APP_VERSION__: JSON.stringify('v1.0.0'),
__API_URL__: 'window.__backend_api_url',
//__DEBUG_MODE__: JSON.stringify(true),
},
// vite中define数据和环境变量有什么区别?
// 1、define,在构建时,静态地注入到代码中
// 2、环境变量,在不同环境下,项目参数可能变化
// 3、环境变量的优先级高于define
plugins: [
createVitePlugins(),
viteMockServe({
// 问:在vite.config.js中,在plugins中引入mock.js,devServer.proxy还有效吗
// 答:1个请求用3种方案中的1种来处理,优先级从大往小为,devServer.proxy、Mock.js、直接发出(浏览器跨域)
// 在“开发环境或生产环境”中,请求静态资源时,下面配置为true的,会被模拟
mockPath: "./src/mock/",//导入此路径下的-虚拟数据
prodEnabled: false,//生产环境,禁用-模拟数据-服务
localEnabled: VITE_USE_MOCK,//true,开发环境,启用-本地模拟数据-服务,network有相关记录
}),
],
build: {
outDir: VITE_OUTPUT_DIR
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/assets/css/variables.scss" as *;`,
},
},
},
resolve: {
alias: {//配置别名,定义路由字符串,路由定义,
'@': path.resolve(__dirname, './src')//将所有的参数拼接到一起
},
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
},
server: {
port: 80,
host: true,
// host:true与host:'0.0.0.0',效果一样,1、当前设备可访问,2、外部设备可通过当前设备的IP地址访问
// host:'localhost'与host:'127.0.0.1',效果一样,只能当前设备访问
open: true,
proxy: {
// 以下3种匹配可以共存,如果多个规则匹配中同一个请求,只有先定义的规则生效。字符串和正则都是匹配path的起始部分
'/app': { //这是统一代理,非常重要!!!
target: 'https://reg.cctv.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/app/, ''),
// rewrite: {"^/app": ""}, //前缀匹配'/api'等效于正则匹配'^/api'
},
'^/(play|create_task|task_status)': { //这是统一代理,非常重要!!!
target: 'http://192.168.116.111:6000',
changeOrigin: true,
},
'/download':{ //这是单个匹配
target: 'http://192.168.116.222:8000',
changeOrigin: true,
},
}
},
}
})
十三、Rollup,JS模块打包工具
1、Rollup的功能
(1)Tree-Shaking,将没用到的代码剔除
(2)兼容CommonJS模块
(3)兼容ES模块
2、配置文件
(1)配置文件示例,rollup.config.js
import json from“ rollup - plugin - json”
import resolve from“ rollup - plugin - node - resolve”
import commonjs from‘ rollup - plugin - commonjs’
export default {
//input:”src/index.js”, //指定入口文件路径
//多入口打包
//input:[‘src/index.js’,’src/album.js’],
input: {
foo: ’src / index.js’,
bar: ’src / ablum.js’
},
output: {
//file:”dist/bundle.js”, //指定输出的文件名
//format:”iife”, //指定输出的格式
//下述是代码拆分需要使用的模式
dir: ’dist’,
format: ’amd’
},
plugins: [
json(),
resolve(), //rollup不能让node支持ESM规范,rollup通过resolve,让node支持ESM规范;webpack能让node支持ESM规范
commonjs(),
babel({ babelHelpers: 'bundled' })
]
}
(2)运行配置文件,
rollup,按照以下顺序运行配置文件:rollup.config.mjs -> rollup.config.cjs -> rollup.config.js
rollup --config my.config.js,运行配置文件:my.config.js
3、API,扩展Rollup本身或者进行一些高级操作
(1)rollup.rollup,参数为输入选项对象,返回一个Promise,该Promise解析为具有各种属性和方法的bundle对象
(2)rollup.watch,当它检测到磁盘上某个模块已经改变,它会重新构建bundle(捆)
4、其他,在模块化方面,browserify和node都用commonjs规范(require导入,module.exports导出),ES6用ESM规范(import导入,exports导出)
十四、前端框架和环境!!!
附、软件发布前的三个步骤
(1)内测,Alpha(α,阿尔法)
(2)公测,Beta(β,贝塔)
(3)正式发布,Gamma(γ,伽玛)
1、JS版本
(1)1995年,发布JavaScript语言;美国人布兰登·艾克创造
(2)1997年,发布ECMAscript1
(3)1999年,发布ECMAscript3
(4)2009年,发布ECMAscript5
(5)2012年,发布TypeScript
(6)2015年,发布ECMAscript6
2、Photoshop各版本
(1)经典版本(早期)
A:1990年:Photoshop 1.0(首个正式版,仅支持 Mac)
B:1991年:Photoshop 2.0(引入路径功能)
C:1994年:Photoshop 3.0(首次支持图层)
D:1996年:Photoshop 4.0(动作功能、调整图层)
E:1998年:Photoshop 5.0(历史记录、文字编辑)
F:2000年:Photoshop 6.0(矢量形状、图层样式)
G:2002年:Photoshop 7.0(修复画笔、文本增强)
(2)Creative Suite(CS)时代
A:2003年:Photoshop CS(版本 8.0,Camera Raw 支持)
B:2005年:Photoshop CS2(版本 9.0,智能对象、红眼工具)
C:2007年:Photoshop CS3(版本 10.0,优化 Intel Mac 支持)
D:2008年:Photoshop CS4(版本 11.0,3D 功能增强)
E:2010年:Photoshop CS5(版本 12.0,内容识别填充)
F:2012年:Photoshop CS6(版本 13.0,暗色界面、视频编辑)
(3)Creative Cloud(CC)时代,(转为订阅制,按年份命名)
A:2013年:Photoshop CC(版本 14.0,智能锐化、相机防抖)
B:2014年:Photoshop CC 2014(版本 15.0,路径模糊、焦点模糊)
C:2015年:Photoshop CC 2015(版本 16.0,设计空间模式)
D:2017年:Photoshop CC 2017(版本 18.0,SVG 字体支持
E:2018年:Photoshop CC 2018(版本 19.0,可变字体、弯度钢笔工具)
F:2019年:Photoshop CC 2019(版本 20.0,内容识别填充增强)
G:2020年:Photoshop 2020(版本 21.0,对象选择工具、云文档)
H:2021年:Photoshop 2021(版本 22.0,天空替换、Neural Filters)
I:2022年:Photoshop 2022(版本 23.0,悬停自动蒙版、共享协作)
J:2023年:Photoshop 2023(版本 24.0,AI 生成式填充(Firefly))
K:2024年:Photoshop 2024(版本 25.0,更新AI 生成式填充(Firefly 2.0)、改进的对象选择工具)
3、angular版本,Google发行
(1)AngularJS,首版,2009年
(2)AngularJS,1.0版,2010年,
(3)Angular,2.0版,2016年09月
(4)Angular,4.0版,2017年03月
(5)Angular,5.0版,2017年11月
(6)Angular,6.0版,2018年05月
(7)Angular,7.0版,2018年10月
(8)Angular,8.0版,2019年05月
(9)Angular,9.0版,2020年02月
(10)Angular,10.0版,2020年06月
(11)Angular,11.0版,2020年11月
(12)Angular,12.0版,2021年05月
(13)Angular,13.0版,2021年12月
(14)Angular,14.0版,2022年06月
(15)Angular,15.0版,2022年11月
(16)Angular,16.0版,2023年05月
4、vue版本
来源,https://github.com/vuejs/core/blob/main/CHANGELOG.md
(1)vue,1.0.0版,2015年10月27日
(2)vue,2.0.0版,2016年10月01日
(3)vue,3.0.0版,2020年01月04日,预发布,setup(props, context)作为组件选项
A、vue,3.0.0-beta.20,2020年07月08日,此前setup(props, context)作为组件选项,替代beforeCreate和created,与vue2的组件写法并存
B、vue,3.0.0-beta.21,2020年07月14日,此后通过<script setup>简化了该组件选项的写法,与vue2的组件写法二选一
(4)vue,3.0.0版,2020年09月18日,正式发布
(5)vue,3.1.0版,2021年06月07日
(6)vue,3.2.0版,2021年08月09日,<script setup> 正式使用
(7)vue,3.3.0版,2023年05月11日
(8)vue,3.4.0版,2023年12月29日
附、vue-cli版本
(1)vue-cli,3.0.0版,2018年08月10日
(2)vue-cli,4.0.0版,2019年10月16日
(3)vue-cli,4.5.0版,2020年07月24日,开始默认使用vue3
(4)vue-cli,5.0.0版,2022年02月17日
附、uni-app版本
(1)2018年8月,uni-app1.0.0 版本发布,
(2)2021年9月,uni-app2.0.0 版本发布,
(3)2023年1月,uni-app3.0.0 版本发布,
5、react版本,FaceBook发行
附、2016年10月,发布Next.js,它是React的框架
来源,https://github.com/facebook/react/releases
react中国,https://react.docschina.org
所有版本简介,https://github.com/facebook/react/blob/main/CHANGELOG.md#1702-march-22-2021
reactdom版本,https://cdn.bootcdn.net/ajax/libs/react-dom/16.6.0/cjs/react-dom.development.js
(1)React,0.3.0版,2013年05月29日
(2)React,0.14.8版,2016年03月29日
(3)React,15.0.0版,2016年04月07日
A、15.1.0版,出现错误边界,error boundaries
B、15.2.3版,出现纯函数组件,PureComponent
(4)React,16.0.0版,2017年09月26日,
A、新增componentDidCatch(--记录错误--)
B、新增纤维fiber架构,
C、弃用旧虚拟DOM,
D、解决了递归调用无法中断和卡顿掉帧的问题
(5)React,16.3.0版,2018年03月29日,
A、沿用旧生命周期componentWillMount,componentWillReceiveProps,componentWillUpdate,
B、新增新生命周期getDerivedStateFromProps,getSnapshotBeforeUpdate,
C、沿用方案、新增方案只能二选一,
D、组件自身state更新,shouldComponentUpdate()>render()>getSnapshotBeforeUpdate()>componentDidUpdate()
E、传递过来的props更新,getDerivedStateFromProps()>shouldComponentUpdate()>render()>getSnapshotBeforeUpdate()>componentDidUpdate()
(6)React,16.4.0版,2018年06月24日,
A、新增Suspense(--组件--)
(7)React,16.6.0版,2018年10月23日,
A、新增getDerivedStateFromError(--处理错误--)
B、新增React.memo()
C、新增React.lazy()
(8)React,16.8.0版,2019年02月06日
A、新增钩子函数Hooks,可以
B、避免组件继承React实例
C、实现状态管理
D、弃用生命周期
(9)React,17.0.0版,2020年10月20日
A、并没有添加任何面向开发人员的新特性
(10)React,18.0.0版,2022年03月29日,
A、新增useTransition(--处理过渡--)
(11)React,18.3.1版,2024年04月26日,最新版本
6、SvelteJS,2016年创建,解决传统前端框架在运行时性能上的瓶颈 // /svelt/苗条的,纤细的;https://www.svelte.cn/
7、SolidJS,2018年创建,制作交互式Web应用程序的JS框架 // /ˈsɒlid/坚固的,实心的;https://www.solidjs.cn/
(1)3个核心API,createSignal、createMemo、createEffect,以createEffect为例,实现逐词插入数据,类似于发报效果
import { createEffect, onMount } from 'solid-js';
import { Marked } from '@ts-stack/markdown';
type Props = {
message: MessageType;
chatflowid: string;
};
Marked.setOptions({ isNoP: true });
export const BotBubble = (props: Props) => {
let botMessageEle: HTMLDivElement | undefined;
onMount(() => { });
var i = 0;
createEffect(() => {
if (botMessageEle) {
let before = props.message.message;
let reg = /"answer": "(.+)",/g;
let after = [];
if(!reg.test(before)){
after.push(before)
}else{
before.replace(reg,function(regAll,A1){
after.push(A1)
});
}
const interval = setInterval(() => {
botMessageEle.innerHTML = Marked.parse(after[i]);
i++;
if(i>after.length-2)clearInterval(interval)
},100);
}
});
return (
<div class="flex flex-col justify-start">
{props.message.message && (
<span
ref={botMessageEle}
/>
)}
</div>
);
};
(2)3个生命周期,onMount(可以在此处向后台请求数据)、onCleanup、onError
(3)PascalCase,帕斯卡命名法,将变量的所有单词的首字母大写
(4)splitProps,属性的使用
//以下案例来源,chat-embed项目下ShortTextInput.tsx文件
//Omit<Type, Keys>TypeScript创建新类型,从现有类型(Type)中排除指定属性(Keys)
import { splitProps } from 'solid-js';
import { JSX } from 'solid-js/jsx-runtime';
type ShortTextInputProps = {
ref: HTMLInputElement | HTMLTextAreaElement | undefined;
onInput: (value: string) => void;
fontSize?: number;
disabled?: boolean;
} & Omit<JSX.TextareaHTMLAttributes<HTMLTextAreaElement>, 'onInput'>;
export const ShortTextInput = (props: ShortTextInputProps) => {
const [local, others] = splitProps(props, ['ref', 'onInput']);
const handleInput = (e) => {
if (props.ref) {
e.currentTarget.scrollTo(0, e.currentTarget.scrollHeight);
local.onInput(e.currentTarget.value);
}
};
return (
<textarea
ref={props.ref}
onInput={handleInput}
{...others}
/>
);
};
//以下案例来源,chat-embed项目下TextInput.tsx文件
import { ShortTextInput } from './ShortTextInput';
export const TextInput = (props: Props) => {
let inputRef: HTMLInputElement | HTMLTextAreaElement | undefined;
return (
<ShortTextInput
ref={inputRef as HTMLTextAreaElement}
onInput={handleInput}
/>
);
};
8、node,介绍!!!
来源,https://nodejs.org/zh-cn/download/releases/
来源,https://nodejs.org/en/blog/release/page/1
来源,https://pnpm.io/zh/motivation
(1)node安装包、node.js、node.exe
A、node安装包,
a、运行后会启动安装向导
b、在系统中部署完整的JS运行时环境node.js
c、node.msi是官方提供的Windows安装包
d、node.tar.xz是官方提供的Linux二进制包
B、node.js
a、是基于Chrome V8引擎的JS运行时环境
b、支持JS在服务器端运行
C、node.exe
a、是node.js的核心可执行文件
b、作为JS解释器,为前后端提供统一的执行环境
D、在前端开发中
a、node.js提供工具链支持,如通过npm管理依赖包
b、node.js环境驱动Webpack、Vite等工具实现代码打包与热更新
E、在后端开发中
a、node.js通过Express等框架开发API
b、node.js环境处理HTTP请求与数据库交互
(2)node版本,内含npm版本,发布年份(发行时间)
附、LTS,“Long-Term Support”的英文缩写,意为长期支持
A、2009年,node出现
B、2010年,npm出现(Node Package Manager,node包管理器)
C、2011年,Windows版node出现,(node.js框架Express.js出现)
D、2012年,原作者离开
E、2013年,Node发布0.10版本
F、2014年,Node发布0.12版,(node.js框架Koa.js出现)
G、2015年,Node发布4.0.0版、5.0.0版,内含npm的2.14.2版、3.3.6版
H、2015年,node发布4.0.0版、5.0.0版,内含npm的2.14.2版、3.3.6版
I、2016年,node发布6.0.0版、7.0.0版,内含npm的3.8.6版、3.10.8版
J、2017年,node发布8.0.0版、9.0.0版,内含npm的5.0.0版、5.5.1版
K、2018年,node发布10.0.0版、11.0.0版,内含npm的5.6.0版、6.4.1版
L、2019年,node发布12.0.0版、13.0.0版,内含npm的6.9.0版、6.12.0版
node13.2.0开始支持ES6模块,此后无需用vue-loader把.vue文件转化为ES6模块
M、2020年,node发布14.0.0版、15.0.0版,内含npm的6.14.4版、7.0.2版
N、2021年,node发布16.0.0版、17.0.0版,内含npm的7.10.0版、8.1.0版
O、2022年,node发布18.0.0版、19.0.0版,内含npm的8.6.0版、8.19.2版
P、2023年,node发布20.0.0版、21.0.0版,内含npm的9.6.4版、10.2.0版
Q、2024年,node发布22.0.0版、23.0.0版,内含npm的10.5.1版、10.9.0版
9、nvm,node版本管理工具,Node Version Manager
(1)发布
A、2010年,美国开发者(网名 creationix)创建
B、2015年,Windows版首版本诞生
(2)下载与安装
A、下载,https://gitcode.com/gh_mirrors/nv/nvm-windows/releases
B、安装,C:\Users\Haier\AppData\Roaming\nvm
(3)设置镜像
A、nvm node_mirror https://npmmirror.com/mirrors/node/
B、nvm npm_mirror https://npmmirror.com/mirrors/npm/
(4)常见命令
A、nvm -v,查看nvm版本号;node -v,查看当前node版本号
B、nvm list available,能安装的版本(不完整)
C、nvm install 16.20.0,安装版本-node@16.20.0
D、nvm use 16.20.0,使用版本-node@16.20.0
E、nvm ls(list),已安装版本列表,前面带*号的为在用版本
(5)上面命令,在vscode编辑器里运行更有效,也可以“以管理员身份运行”
A、点击任务栏上的放大镜,右键“命令提示符”,点击“以管理员身份运行”
B、点击任务栏上的放大镜,输入cmd,点击“以管理员身份运行”
10、npm,node包管理工具,Node Package Manager
(1)淘宝npm镜像使用方法
来源,https://blog.csdn.net/quuqu/article/details/64121812
A、临时使用npm镜像
a、npm --registry https://registry.npm.taobao.org install express
b、验证 npm info express
B、持久使用npm镜像
a、配置,npm config set registry https://registry.npm.taobao.org
b、验证,npm config get registry
C、持久使用pnpm镜像
a、配置,pnpm config set registry https://registry.npm.taobao.org/</script>
b、验证,pnpm config get registry
(2)node包管理工具之npm、cnpm,yarn、tyarn,pnpm
A、npm:node.js自带的包管理工具
a、2010年发布
b、生成package-lock.json文件,从npm5.0(内置于2017年发布的node8.0.0)开始
c、按顺序依次下载每个包,速度慢
d、下面4个包管理工具可通过npm或手动安装
d1、npm安装。npm install xxx
d2、手动安装。从工具的官方渠道下载安装资源,用电脑系统包管理器安装
B、cnpm:即c(china,中国)npm
a、2014年发布,淘宝推出的npm替代工具
b、不生成lock文件
c、安装,npm install -g cnpm --registry=https://registry.npm.taobao.org
C、yarn:纱线、故事
a、2016年脸书谷歌等联合发布,并行下载包,速度块
b、生成yarn.lock文件
D、tyarn:即t(taobao,淘宝)yarn
a、2022年发布,淘宝推出的yarn替代工具
b、不生成lock文件
E、pnpm:即p(performant,高性能的)npm
a、2016年11月发布,乌克兰人Zoltan Kochan推出的npm和yarn的改进工具
b、将不同版本间有差异的文件添加到仓库,不会因为一个文件的改变,而复制整个新版本包的内容
c、所有文件都会存储在硬盘上的某一位置,允许跨项目地共享同一版本的依赖,避免重复安装相同的依赖
d、生成pnpm-lock.yaml文件
(3)npm install 安装命令示例,包名和后面的参数,位置可以互换
A、npm install 把package.json中的(开发devDependencies|开发生产dependencies)依赖配置,下载到本地项目的node_modules
B、npm install X 会把X包安装到项目的node_modules目录中,不会将模块依赖写入package.json中
如果package.json中存在X包,本地没有安装,那么运行此命令,会从远程仓库下载package.json指定版本的X包
C、npm install X -g 会把X包安装到全局的node_modules目录中,不会将模块依赖写入package.json中
D、npm install X --save 会把X包安装到项目的node_modules目录中,会在package.json的dependencies属性下添加X,--save简写为-S
E、npm install X@3.10.3 --save-dev 会把X包安装到项目的node_modules目录中,会在package.json的devDependencies属性下添加X,--save-dev简写为-D
(4)npm其它命令
A、npm help <command>可查看某条命令的详情
B、npm list <package>可以检测当前目录下node_modules子目录里边有没有这个包
C、npm update <package>可以把当前目录下node_modules子目录里边的对应模块更新至最新版本
D、npm run start/stop/test可以简写为npm start/stop/test
E、获取npm的安装路径,npm config get prefix
(5)包的运行
A、npm,必须在根目录下运行包,必须写入到package.json里面,且已安装
B、npx,可以在任何地方运行包,不必写入到package.json里面,不必安装,从本目录开始逐层向上直至全局(nodejs环境变量)寻找该包,
a、找到就运行,没有找到就临时安装并直接运行,运行结束后删掉;
b、如果想运行层级以外的版本,则加上版本号即可,如npx webpack-dev-server@3.10.3
(6)“npm run dev”的运行说明
{
"scripts": {
"dev": "xxx abc",
"serve": "vue-cli-service serve",
},
}
注意:直接执行“xxx abc”命令会报错,因为操作系统里只有npm相关的命令
A、在运行“npm install xxx”时,会在“node_modules/.bin”目录下,为包“xxx”创建名为“xxx”的3个文件,三者都是用node执行1个js文件
a、没有后缀名的是对应Unix系的shell脚本,
b、.cmd为后缀名的是windows bat脚本,
c、.ps1为后缀名的则是PowerShell脚本(可以跨平台)
B、“npm run dev”运行时,
a、npm首先在"scripts"字段里,找到“dev”对应的“xxx”字段,
b、然后在“node_modules/.bin”目录下,找到名为“xxx”的文件,该文件从“node_modules/xxx”文件里引入相应文件,并运行"xxx abc"
C、运行后,缺包(出错、报错、错误)排查
a、node版本是否太新或太旧
b、本公司的公共包是否下载到对应位置
11、与node相关!!!(导出与导入、路径符号与url分隔符、path.join()与path.resolve()、@说明、@定义、url解析)
(1)导出与导入
A、node模块块开发:导出模块module.exports = {},导入模块require('./url/moduleName')
B、ES6模块块开发:导出模块export default xxx,导入模块import xxx from './url/moduleName'
(2)文件路径符与url分隔符
A、文件路径符
a、\反斜杠,主要用于window系统,如\表示路径分隔符,.\表示当前目录,..\表示上级目录
b、/正斜杠,主要用于非window系统,如/表示路径分隔符,./表示当前目录,../表示上级目录
B、url分隔符
a、/正斜杠,用于一切系统的url中,如/表示网站根目录
b、当请求的路径是/blog/article时,服务器会在文件系统中找,/var/www/html + /blog/article = /var/www/html/blog/article
(3)path.join()与path.resolve()的区别
A、path.join() //从左到右拼接路径,对最终路径进行规范化处理,如解析.、..,统一分隔符
B、path.resolve() //从右向左拼接路径,直到遇到第一个绝对路径,如C:\、\、/为止;没有则以当前工作目录作为起点
C、示例
const path = require('path');
console.log(__dirname); //c:\Users\Haier\Desktop\z_vue
console.log(path.join()); //.
console.log(path.join('/a', '/b')); //\a\b
console.log(path.join(__dirname, './src')); //c:\Users\Haier\Desktop\z_vue\src
console.log(path.resolve()); //c:\Users\Haier\Desktop\z_vue
console.log(path.resolve('/a', '/b')); //c:\b
console.log(path.resolve(__dirname, './src')); //c:\Users\Haier\Desktop\z_vue\src
(4)@说明
A、~是构建工具的特殊标记,@是构建工具中配置的路径别名,~@告诉构建工具按项目配置的路径规则查找资源
B、在html中,使用@
C、在CSS的SCSS/Sass/Less预处理器的@import语法中,对@有专门定义,因此后面必须使用~@,在CSS的其它情况下最好使用~@
D、@与~@使用示例
<img src="@/assets/logo.png" alt="" />
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
.bg {
background: url(@/assets/logo.png) no-repeat;
}
</style>
(5)@定义示例
module.exports = {
context: '/',
entry: {},
output: {},
resolve: {
alias: { //别名
//以下解析方式相同
'@': path.join(__dirname, '..', 'src')
'@': path.join(__dirname, '../src')
}
},
}
export default defineConfig(({command,mode})=>{
return {
resolve: {
alias: { //别名
//以下键名写法相同,但带引号更安全
@: path.resolve(__dirname, './src')
'@': path.resolve(__dirname, './src')
},
},
}
})
(4)url解析,问号传参给服务器,井号传参给浏览器
var url_ = 'http://www.zhu.cn:80/ccc/index.html?name=zxt&age=26#33';
var url = require('url');
console.log(url.parse(url_, true));
/* winUrl{
protocol: 'http:',
slashes: true,
auth: null,
host: 'www.zhu.cn:80',
port: '80',
hostname: 'www.zhu.cn',
hash: '#33',
search: '?name=zxt&age=26',
query: { name: 'zxt', age: '26' },//当true不存在时query: 'name=zxt&age=26',
pathname: '/ccc/index.html',
path: '/ccc/index.html?name=zxt&age=26',
href: 'http://www.zhu.cn:80/ccc/index.html?name=zxt&age=26#33'
} */
12、npm运行出错与解决
(1)问题1
A、现象,运行npm install,下面情况导致报错
"dependencies": {
"swiper": "^8.1.0",//一级插件
"vue-awesome-swiper": "^4.1.1",//内部有二级插件swiper,但版本为"^5.2.0"
},
B、解决
初步解决:更改一级以适应二级,问题消失,但安装结束后,node_modules被自动删除;
最终解决:运行“yarn”,彻底解决这个问题
(2)问题2
A、现象,运行npm run dev,页面卡顿
B、解决,把mock文件夹里,新生成的压缩文件如fkdsfsdlsdf.js删除;或者运行node -v,看node版本是不是在16以上
(3)问题3
A、现象,运行npm run dev
跳转到localhost/#/sys/score,可以打开页面
跳转到localhost:81/#/sys/score,不可以打开页面
B、解答,以前开发时
在80端口设置了永久标志,在81端口没设置永久标志
用router.beforeEach拦截时,前者通过,后者卡住
(4)问题4
A、现象,运行npm run dev,报错如下
[Vue warn]: Invalid prop: custom validator check failed for prop "type". vue.runtime.esm.js?
B、解决
A、报错原因,iview支持数字类型的输入,vue自带的vue.runtime.esm.js不支持数字类型的输入
B、解决方案,在浏览器安装https://github.com/vuejs/vue-devtools,取代vue.runtime.esm.js,给vue检查错误
C、该错误不影响运行
13、Live Server,一个快速启动的本地服务器
(1)在vscode的扩展中找到并安装
(2)在项目中-使用步骤
A、右键根目录下index.html
B、点击Open with Live Server
C、改动该项目任意文件的内容,保存,自动刷新
(3)在demo中-使用步骤
A、右键任意.html文件
B、点击Open with Live Server
C、改动该文件的内容,保存,自动刷新(无需点击浏览器的圆箭头)
(4)主要功能
A、实时重新加载,自动检测文件变化,并立即在浏览器中刷新页面
B、跨域代理(需配置)
(5)地址栏显示,
A、Open with Live Server,http://127.0.0.1:5500/0.html
B、Open In Default Browser,file:///C:/Users/Haier/Desktop/z_vue/0.html
14、yapi,可本地部署的API管理平台
(1)下载
A、nodejs(7.6+)
B、mongodb(2.6+),默认安装在C盘,应改在D盘,且路径中的目录名不能有空格
(2)安装步骤及注意事项
A、卸载nodejs,
B、删除npm,C:\Users\XXXXX\AppData\Roaming\npm
C、安装node,遇到“Custom Setup”时,选择“npm package manager”
D、安装mongodb,遇到“Installing MongoDB Compass”时,去掉默认的勾选
E、每次重新搭建yapi之前,需要删除my-yapi目录、卸载并安装1次mongodb
(3)安装并运行yapi
A、安装yapi脚手架,npm install -g yapi-cli --registry https://registry.npm.taobao.org
B、搭建yapi项目,yapi server,想把yapi搭建在哪个目录下,就在哪个目录下运行此命令。
C、访问yapi项目,http://127.0.0.1:9090/
D、配置并连接数据库,即填写公司名称、点击开始部署
E、启动yapi,在my-yapi目录下运行node vendors/server/app.js
--另、在谷歌浏览器上装corss-request插件后,可以在在yapi上发送请求
(4)登录
A、http://127.0.0.1:3000(仅限自己)或者http://172.18.10.12:3000(自己和别人)
B、登录/注册,选登录
C、账号名:"admin@admin.com",
D、密码:"ymfe.org"
E、登录
(5)yapi的层级
A、分组
B、项目
C、分类
D、接口
十五、微前端
1、介绍
(1)Micro Frontends,是一种前端架构模式,将大型Web应用拆分成多个独立、自治的小型前端应用,称为“微应用”
(2)通过框架集成这些微应用,形成一个统一的用户体验
(3)它借鉴了后端微服务的思想,旨在解决传统单体前端应用在团队协作、技术栈升级、代码维护等方面的痛点
2、核心思想
(1)拆分:按业务功能或团队边界拆分前端应用,每个微应用独立开发、测试、部署
(2)自治:微应用可使用不同技术栈(如 React、Vue),拥有独立的生命周期
(3)集成:通过主框架(如 single-spa、Qiankun)将微应用整合到统一界面中
3、关键特点
(1)技术栈无关,不同微应用可使用不同框架(如 React 与 Vue 共存),逐步升级技术栈
(2)独立部署,微应用可单独开发、测试、上线,无需协调整个项目
(3)团队自治,每个微应用由独立团队负责,减少跨团队协作成本
(4)增量升级,逐步替换旧模块,避免 “全量重构” 风险
(5)代码隔离,微应用间共享状态可控,减少命名冲突和依赖问题
4、常见实现方式
(1)路由分发式:主框架通过路由规则将请求分发给不同微应用(如 single-spa)
(2)iframe 集成:使用 iframe 嵌入子应用,隔离性强但通信复杂
(3)组件加载式:动态加载微应用的组件(如 Web Components)
(4)构建时集成:通过构建工具(如 Module Federation)在编译阶段整合微应用(Webpack 5 支持)
5、典型场景
(1)大型企业应用:如后台管理系统,不同模块由不同团队维护
(2)多技术栈共存:旧项目使用 AngularJS,新项目想迁移到 React/Vue
(3)多团队协作:多个前端团队并行开发,避免代码冲突
(4)渐进式升级:逐步重构旧系统,而非推倒重来
6、优缺点
(1)优点
A、提高开发效率,降低团队协作成本
B、技术栈灵活,支持按需升级
C、故障隔离,单个微应用崩溃不影响整体
(2)缺点
A、架构复杂度增加,需要统一的规范和工具链
B、跨应用通信和状态管理更复杂
C、性能开销:多个微应用可能增加资源加载时间
7、相关工具
(1)single-spa:JavaScript 微前端框架,支持多种框架集成
(2)Qiankun:蚂蚁集团开源的微前端框架,基于 single-spa,中文文档完善
(3)Webpack Module Federation:Webpack 5 内置的模块联邦,支持运行时动态加载
(4)MicroApp:轻量级微前端框架,主打高性能和简单易用
8、常见实现方式-路由分发式
附、豆包:路由分发式,主框架通过路由规则将请求分发给不同微应用。请给出目录结构和主应用、微应用入口文件示例
(1)目录结构
A、前端根目录结构
root-project/ # 根项目(主应用)
├── package.json
├── webpack.config.js # 主应用构建配置
├── public/
│ └── index.html # 主应用 HTML 模板
└── src/
├── index.js # 主应用入口:注册微应用
├── App.js # 主应用根组件
├── router/ # 主应用路由配置
│ └── index.js
└── micro-apps/ # 微应用配置
├── config.js # 微应用注册配置
└── utils.js # 微应用工具函数
micro-apps/ # 微应用(独立项目,可单独仓库)
├── goods-app/ # Vue 微应用(商品列表)
│ ├── package.json
│ ├── src/
│ │ ├── main.js # 微应用入口:暴露生命周期
│ │ ├── router/
│ │ │ └── index.js # 微应用路由配置
│ │ └── App.vue
│ └── webpack.config.js # 微应用构建配置
├── cart-app/ # React 微应用(购物车)
│ ├── package.json
│ ├── src/
│ │ ├── index.js # 微应用入口:暴露生命周期
│ │ ├── App.js
│ │ └── routes.js
│ └── webpack.config.js
└── user-app/ # Angular 微应用(用户中心)
├── package.json
├── src/
│ ├── main.ts # 微应用入口:暴露生命周期
│ ├── app/
│ │ ├── app.module.ts
│ │ └── app.routing.ts
│ └── qiankun.ts # 乾坤生命周期封装
└── angular.json
B、服务器根目录结构
/var/www/html/ # 服务器根目录(对应域名:https://example.com)
├── main-app/ # 主应用部署目录
│ ├── index.html # 主应用入口 HTML
│ ├── static/ # 主应用静态资源
│ │ ├── js/ # 主应用打包 JS
│ │ └── css/ # 主应用打包 CSS
│ └── favicon.ico # 主应用图标
├── micro-apps/ # 微应用统一存放目录
│ ├── goods-app/ # Vue 微应用(商品列表)
│ │ ├── index.html # 微应用入口 HTML
│ │ ├── js/ # 微应用打包 JS
│ │ └── css/ # 微应用打包 CSS
│ │
│ ├── cart-app/ # React 微应用(购物车)
│ │ ├── index.html
│ │ ├── js/
│ │ └── css/
│ │
│ └── user-app/ # Angular 微应用(用户中心)
│ ├── index.html
│ ├── js/
│ └── css/
└── nginx.conf
C、Nginx 配置(路由转发核心)
# nginx.conf
server {
listen 80;
server_name example.com;
root /var/www/html;
# 主应用路由:直接指向主应用目录
location / {
root /var/www/html/main-app;
try_files $uri $uri/ /index.html; # 支持 SPA 路由
}
# 微应用路由转发:/goods → 指向 goods-app 目录
location /goods {
alias /var/www/html/micro-apps/goods-app;
try_files $uri $uri/ /goods/index.html; # 微应用 SPA 路由支持
}
# 微应用路由转发:/cart → 指向 cart-app 目录
location /cart {
alias /var/www/html/micro-apps/cart-app;
try_files $uri $uri/ /cart/index.html;
}
# 微应用路由转发:/user → 指向 user-app 目录
location /user {
alias /var/www/html/micro-apps/user-app;
try_files $uri $uri/ /user/index.html;
}
}
(2)主应用核心代码
A、微应用配置(src/micro-apps/config.js)
// 微应用配置(路由规则与微应用映射)
export default [
{
name: 'goods-app',
entry: process.env.NODE_ENV === 'development'
? '//localhost:8081' // 开发环境
: '/micro-apps/goods-app/', // 生产环境
container: '#micro-app-container',
activeRule: '/goods', // 关键:路由匹配规则
props: { /* 传递给微应用的参数 */ },
},
{
name: 'cart-app',
entry: process.env.NODE_ENV === 'development'
? '//localhost:8082'
: '/micro-apps/cart-app/',
container: '#micro-app-container',
activeRule: '/cart',
},
{
name: 'user-app',
entry: process.env.NODE_ENV === 'development'
? '//localhost:8083'
: '/micro-apps/user-app/',
container: '#micro-app-container',
activeRule: '/user',
},
];
B、主应用入口(src/index.js)
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import { registerMicroApps, start } from 'qiankun';
import microApps from './micro-apps/config'; // 微应用配置
// 注册微应用
registerMicroApps(microApps, {
beforeLoad: (app) => console.log(`Loading app: ${app.name}`),
beforeMount: (app) => console.log(`Mounting app: ${app.name}`),
afterUnmount: (app) => console.log(`Unmounting app: ${app.name}`),
});
// 启动 Qiankun
start();
// 渲染主应用
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
C、主应用根组件(src/App.js)
import { Link, Routes, Route } from 'react-router-dom';
function App() {
return (
<div className="main-app">
{/* 主应用导航 */}
<nav>
<Link to="/goods">商品列表</Link> |
<Link to="/cart">购物车</Link> |
<Link to="/user">用户中心</Link>
</nav>
{/* 微应用挂载容器 */}
<div id="micro-app-container" />
{/* 主应用自有路由(可选) */}
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</div>
);
}
(3)微应用核心代码
A、Vue 微应用(商品列表)
a、入口文件(goods-app/src/main.js)
import Vue from 'vue';
import App from './App.vue';
import createRouter from './router';
const isMicroApp = window.__POWERED_BY_QIANKUN__;
let instance = null;
function render(props = {}) {
instance = new Vue({
router: createRouter(isMicroApp ? '/goods' : ''),
render: h => h(App),
}).$mount(props.container?.querySelector('#app') || '#app');
}
// 独立运行时直接渲染
if (!isMicroApp) {
render();
}
// 暴露生命周期钩子
export async function bootstrap() {}
export async function mount(props) {
render(props);
}
export async function unmount() {
instance.$destroy();
instance = null;
}
b、路由配置(goods-app/src/router/index.js)
import VueRouter from 'vue-router';
import GoodsList from '../views/GoodsList.vue';
import List from '../views/List.vue';
export default function createRouter(base = '') {
return new VueRouter({
mode: 'history',
base,
routes: [
{ path: '/', name: 'GoodsList', component: GoodsList },
{ path: '/list', name: 'List', component: List },
],
});
}
B、React 微应用(购物车)
入口文件(cart-app/src/index.js)
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
// 微应用生命周期
let root = null;
function render(props) {
const { container } = props;
root = ReactDOM.createRoot(
container ? container.querySelector('#root') : document.getElementById('root')
);
root.render(
<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/cart' : '/'}>
<App />
</BrowserRouter>
);
}
// 独立运行时直接渲染
if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
// 暴露生命周期钩子
export async function bootstrap() {}
export async function mount(props) {
render(props);
}
export async function unmount() {
root.unmount();
root = null;
}
(4)关键机制说明
A、路由匹配流程
a、主应用监听 URL 变化,当路径匹配到 activeRule(如 /goods)时,加载对应微应用
b、微应用内部路由基于 basename(如 /goods),确保子路由正确匹配(如 /goods/list)
B、生命周期管理
a、微应用通过 bootstrap、mount、unmount 钩子与主应用通信,控制渲染和卸载
C、独立运行支持
a、微应用通过 window.__POWERED_BY_QIANKUN__ 判断运行环境,支持独立开发和调试
(5)部署建议
A、主应用部署在根路径(如 https://example.com)
B、微应用分别部署在子路径(如 https://example.com/micro-apps/goods-app/)
C、生产环境通过 Nginx 等服务器配置路径转发,确保资源正确加载
十六、神策(本文档基于1.26.5版本)
注意,先做项目(了解源码的功能),再看源码(了解功能的实现)
作用,分析用户的行为
来源,https://manual.sensorsdata.cn/sa/latest/zh_cn/tech_sdk_client_web_use-7545024.html
1、集成文档
(1)自动生成
(2)同步载入
(3)CommonJS规范加载
(4)ES6模块化引入
(5)AMD规范加载
2、基础api介绍(属性-注册、获取、设置、上报)
(1)SDK初始化参数,16项
(2)注册公共属性
// 以下项目旧代码备份
// var i=0
// sensors.init({
// server_url: server_url, //神策数据接收地址
// //web_url: web_url, //神策分析后台地址,神策1.10及以上版本,不需要配置这个参数,
// is_track_single_page: true, //单页面配置,默认开启,若页面中有锚点设计,需要将该配置删除,否则触发锚点会多触发 $pageview 事件
// use_client_time: true,
// send_type: 'beacon',
// // heatmap: {
// // clickmap: 'default', //(1/3)开启点击图,自动采集a input button textarea 四种元素的$WebClick事件,'not_collect'表示关闭
// // scroll_notice_map: 'default', //(2/3)开启触达图,自动采集$WebStay事件,'not_collect'表示关闭
// // },
// show_log: true, //console会打印采集信息
// // preset_properties: { //子配置项 true 表示采集,false 表示不采集,未设置的参数取默认值
// // latest_traffic_source_type: false, //是否采集 $latest_traffic_source_type 最近一次流量来源类型
// // latest_search_keyword: false, //是否采集 $latest_search_keyword 最近一次搜索引擎关键字
// // latest_referrer: false, //是否采集 $latest_referrer 最近一次前向地址
// // },
// });
// sensors.registerPage({
// current_url: location.href,
// referrer: document.referrer,
// description1: 'server_url字段为空或非神策地址,在开发者的控制台,依然可以看',
// description2: '到神策插件采集的数据,只是神策服务器接收不到该数据,没法分析!',
// prop_number_: function() {
// return ++i;
// },
// });
// sensors.quick('autoTrack'); //(3/3)用于采集$pageview事件
(3)获取预置属性
sensors.quick('isReady',function(){
var presetProperties = sensors.getPresetProperties();
console.log( presetProperties );
});
(4)设置用户属性
A、直接设置用户的属性,如果存在则覆盖。
sensors.setProfile({email:'xxx@xx'});
B、如果不存在则设置,存在就不设置。
sensors.setOnceProfile({email:'xxx@xx'});
C、给数组属性添加值
sensors.appendProfile({catrgory: ['玉米','白菜']});
sensors.appendProfile({catrgory: '玉米'});//给 category 增加一个值
D、对当前用户的属性做递增或者递减
sensors.incrementProfile({'navClick': -1});//表示navClick递减
E、删除当前用户及他的所有属性
sensors.deleteProfile();
F、删除当前用户的一些属性
sensors.unsetProfile(['email','location']);
sensors.unsetProfile('email');
(5)物品元数据上报
A、直接设置一个物品,如果已存在则覆盖
sensors.setItem("food","2",{name:"玉米",flavour:"甜"});
setItem(item_type,item_id,properties)
除物品ID与物品所属类型外,其他物品属性需在properties中定义
物品属性中,属性名称与属性值的约束条件与事件属性相同
item_type:必选
item_id:必选
properties:可选
B、sensors.deleteItem("food","2");
deleteItem(item_type,item_id)
如果物品不可被推荐需要下线,删除该物品即可,如不存在则忽略
除物品ID与物品所属类型外,不解析其他物品属性
item_type:必选
item_id:必选
3、全埋点(标签埋点、属性埋点)
附1、heatmap相关参数,https://manual.sensorsdata.cn/sa/latest/zh_cn/tech_sdk_client_web_all_use-7545310.html
附2、server_url获取步骤,登录神策-基本设置-数据接入-(客户端埋点)生成导入代码-生成-server_url
附3、示例,全埋点三个事件(元素点击$WebClick、视区停留$WebStay、页面浏览$pageview),其中$WebClick默认采集4种元素(a input button textarea)
<script>
//var sensors = window['sensorsDataAnalytic201505'];
import sensors from 'sa-sdk-javascript';
var server_url = 'https://10.50.16.15/api';
var web_url = 'https://10.50.16.15/api';
var i=0;
sensors.init({
server_url: server_url,// 数据接收地址
web_url: web_url,// 神策分析后台地址,神策1.10及以上版本,不需要配置这个参数,
is_track_single_page: true, // 单页面配置,默认开启,若页面中有锚点设计,需要将该配置删除,否则触发锚点会多触发 $pageview 事件
use_client_time: true,
send_type: 'beacon',
heatmap: {
clickmap:'default',//(1/3)开启点击图,$WebClick事件默认采集4种元素,'not_collect'表示关闭
scroll_notice_map:'default',//(2/3)开启视区停留(触达图),自动采集$WebStay事件,'not_collect'表示关闭
loadTimeout: 3000,
collect_url: function(){
//如果只采集首页
if(location.href === 'example.com/index.html' || location.href === 'example.com/'){
return true;
}
},
//此参数针对预置 $WebClick 事件(包括 quick('trackHeatMap') quick('trackAllHeatMap') 触发的)生效。
collect_element: function(element_target){
// 如果这个元素有属性sensors-disable=true时候,不采集。
if(element_target.getAttribute('sensors-disable') === 'true'){
return false;
}else{
return true;
}
},
//此参数针对预置 $WebClick 事件(包括 quick('trackHeatMap') quick('trackAllHeatMap') 触发的)生效。
custom_property: function( element_target ){
//比如您需要给有 data=test 属性的标签的点击事件增加自定义属性 name:'aa' ,则代码如下:
if(element_target.getAttribute('data') === 'test'){
return {
name:'aa'
}
}
},
collect_input: function(element_target){
if(element_target.id === 'a'){ //如果元素的 id 是a,就采集这个元素里的内容。
return true;
}
},
element_selector: 'not_use_id',
renderRefreshTime: 1000
},
scrollmap: {
collect_url: function(){//如果只采集首页
if(location.href === 'example.com/index.html' || location.href === 'example.com/'){
return true;
}
},
},
show_log: true, // console会打印采集信息
});
sensors.quick('autoTrack'); //(3/3)自动采集$pageview事件
sensors.quick('autoTrack', { //添加额外的属性
platform: 'h5'
})
</script>
(1)扩展至div元素,采集规则为
A、div为叶子结点(无子元素)时采集div的点击
B、div中有且只有样式标签(['mark','strong','b','em','i','u','abbr','ins','del','s','sup'])时,点击div或者样式标签都采集div的点击
heatmap:{
collect_tags:{
div: true
}
}
(2)扩展至任意元素,示例
heatmap: {
clickmap:'default',
collect_tags: {
div: { //div通过配置最多可以采集3层嵌套
max_level: 1 //默认是1,即只支持叶子div。可配置范围是[1, 2, 3],非该范围配置值,会被当作1处理。
},
get_vtrack_config: true, //无限层级的div
li: true,
img: true
//...其他标签
}
}
(3)扩展至特殊属性,示例
<div data-sensors-click>我是测试元素</div>
<li data-sensors-click>我是测试元素</li>
(4)扩展至自定义属性,示例
heatmap: {
clickmap:'default',
track_attr: ['prop1', 'prop2', "prop3"],
}
<p prop1>prop1</p>
<p><span prop2>prop2</span></p>
<p><strong prop3>prop3</strong></p>
(5)代码埋点(这是局部埋点)
示例1,jQuery
<div id="submit_order">提交订单</div>
<script type="text/javascript">
$('#submit_order').on('click', function() { //代码埋点
sensors.quick('trackHeatMap', this, { //触发元素点击事件
customProp1: 'test1', //如果需要添加自定义属性需要将SDK升级到 1.13.7 及以上版本。
customProp2: 'test2'
});
});
</script>
示例2,vue
<div v-on:click="track">点击</div>
<script>
export default {
methods: {
track: function(event) {//代码埋点
sensors.quick('trackHeatMap', event.target, {//触发元素点击事件
customProp1: 'test1', //如果需要添加自定义属性需要将SDK升级到 1.13.7 及以上版本。
customProp2: 'test2'
});
}
}
}
</script>
4、高级功能
附、高级功能清单
A、属性插件化,
B、批量发送,
C、预置属性是否采集,
D、关闭页面时发送数据丢失的解决方案,
E、单页面中事件的自动采集
(1)属性插件化,
作用,给指定的事件添加、修改或删除属性,
实现,registerPropertyPlugin,包含properties和isMatchedWithFilter
如果不配置后者,配置的 properties 方法始终执行
如果配置了后者,仅当该方法返回 true 时,配置的 properties 方法才会得到执行
示例如下
A、直接修改
sensors.registerPropertyPlugin({ //对所有类型数据,修改属性值,这种用法会引发意想不到的情况
properties: function(data){
data.properties['aaa'] = 'bbb';
}
});
sensors.registerPropertyPlugin({ //删除所有事件中的 platform 属性
properties: function(data){
delete data.properties['platform'];
}
});
sensors.registerPropertyPlugin({ //直接在properties里进行筛选和属性修改
properties: function(data){
if(data.event === '$pageview'){
data.properties['$url'] = 'http://xxxx';
}
}
});
B、先筛选后修改
sensors.registerPropertyPlugin({ //修改事件名为 $pageview 下的 $url 属性
isMatchedWithFilter: function(data){
return data.event === "$pageview";
}
properties: function(data){
data.properties['$url'] = 'http://xxx';
}
});
sensors.registerPropertyPlugin({
isMatchedWithFilter: function(data){
return data.type.slice(0, 4) === 'item' || data.type.slice(0, 7) === 'profile';
}
properties: function(data){
delete data.properties['aaa'] = 'bbb';
}
});
(2)批量发送
示例如下
sensors.init({
batch_send:true,//开启批量发送
batch_send:{//或者
datasend_timeout: 6000, //一次请求超过多少毫秒的话自动取消,防止请求无响应。
send_interval: 6000, //间隔多少毫秒发一次数据。
storage_length: 200 //存储localStorage条数最大值,默认:200。如localStorage条数超过该值,则使用image方式立即发送数据。v1.24.8 以上支持。
},
});
写入策略
触发事件就写入localStorage
发送策略
定时触发发送
遇到$pageview、使用quick('autoTrack')、使用$SignUp,立即存储并且发送
重复策略
必须请求success后,才会删除数据,不然会一直请求,直到数据满一定数量
(3)预置属性是否采集
sensors.init({
preset_properties: { //子配置项 true 表示采集,false 表示不采集,未设置的参数取默认值
latest_utm: true, //是否采集 $latest_utm 最近一次广告系列相关参数
latest_traffic_source_type: true, //是否采集 $latest_traffic_source_type 最近一次流量来源类型
latest_search_keyword: true, //是否采集 $latest_search_keyword 最近一次搜索引擎关键字
latest_referrer: true, //是否采集 $latest_referrer 最近一次前向地址
latest_referrer_host: false, //是否采集 $latest_referrer_host 最近一次前向地址,默认值先true后false
latest_landing_page: false, //是否采集 $latest_landing_page 最近一次落地页地址,默认值false。
url: true, //是否采集 $url 页面地址作为公共属性,默认值先false后true
title: true, //是否采集 $title 页面标题作为公共属性,默认值先false后true
},
source_channel:'',//默认获取的来源是根据utm_source等ga标准来的
//utm_source,指定流量的来源,例如搜索引擎、社交媒体网站等
//utm,是Urchin(小脏孩,免费的分析工具) Tracking Module 的简称
});
(4)关闭页面时发送数据丢失的解决方案
A、改为服务端发送事件
在服务端中埋点采集
B、采集跳转后页面的$pageview事件
给A页面的a标签的href属性添加某个参数,如B页面www.xxx.com?urlfrom=123,
跳转到B页面后,采集这个页面的$pageview事件,在神策后台中查看Web浏览事件,根据url是否包含urlfrom参数来筛选结果
C、使用setTimeout
// 点击链接执行
function targetLinkIcon( url ){
setTimeout(function(){ //延迟跳转页面,给 SDK 发送数据提供时间
window.location.href = url;
},500);
sensors.track('demo',{}); //神策自定义事件的方法
}
D、beacon的方式发送数据
配置
sensors.init({
send_type: 'beacon',
});
原理说明
来源,https://blog.csdn.net/Ed7zgeE9X/article/details/131545832
Beacon API是HTML5提供的新型浏览器API,可以在不影响当前页面加载和性能的情况下,在浏览器后台异步发送数据
借助Beacon API,开发人员可以在页面卸载或关闭时向服务器发送数据,从而实现一些监控和日志记录功能
//以下前端代码
var data = "Hello, Beacon API!";
var url = "https://example.com/endpoint";
navigator.sendBeacon(url, data); //导航员.发送信标
//以下后端代码
app.post('/endpoint', function(req, res) {
var data = req.body;
// 处理接收到的数据
// ...
res.sendStatus(200); // 返回响应状态码
});
(5)单页面中事件的自动采集
A、自动模式
sensors.init({
is_track_single_page: true,
is_track_single_page: function (){
return true 时候,使用默认发送的 $pageview
return false 时候,不执行默认的 $pageview
return {} 时候,把对象中的属性,覆盖$pageview里的默认属性
}
});
C、手动模式
在 react 中可以在全局的 onUpdate 里来调用
onUpdate: function(){
sensors.quick('autoTrackSinglePage');
}
vue 项目在路由切换的时候调用
router.afterEach((to,from) => {
Vue.nextTick(() => {
sensors.quick("autoTrackSinglePage");
});
});
//注意:vue下因为首页打开时候就会默认触发页面更新,所以需要去掉默认加的 sa.quick('autoTrack')。
//此方法也可添加自定义属性,sensors.quick("autoTrackSinglePage",{platForm:"H5"});
5、安全合规
(1)安全,
本地存储加密,不支持埋点数据加密
sensors.init({
encrypt_cookie: true //开启cookie加密配置,默认false
});
(2)合规,
延迟初始化
if(同意隐私条款){
sensors.init({});
sensors.quick('autoTrack');
}
动态开启/关闭采集,必须在init后调用
sensors.init({});
sensors.disableSDK()// 禁用API执行
sensors.enableSDK()// 恢复API执行
6、用户关联
(1)简易用户关联,
方法:sensors.login("登录 ID");
时机:
A、用户注册成功时
B、用户登录成功时
C、已登录用户每次启动App时
获取匿名ID
sensors.quick('isReady',function(){
var anonymousID = sensors.quick('getAnonymousID');
});
修改匿名ID
sensors.identify(id, true): 会把这个id保存在浏览器的cookie中,该域名下的页面都会默认使用这个id
(2)全域用户关联
方法
sensors.login("登录 ID");
时机
A、用户注册成功时
B、用户登录成功时
多用户ID关联,
sensors.bind("$identity_mobile","187****8991"),后续采集的事件,均包含缓存的ID信息
第1个参数从详细的预置id key列表中获取,https://manual.sensorsdata.cn/sa/latest/zh_cn/tech_sdk_client_web_idm3-109576372.html
第2个参数为对应的关联用户ID
多用户ID取消关联,
sensors.unbind("$identity_mobile","187****8991")
重置匿名ID,
sensors.resetAnonymousIdentity();
sensors.resetAnonymousIdentity('id-xxxxxxx-xxxxx'); // 修改为指定的匿名ID
获取全域用户的ID,
sensors.getPresetProperties()
sensors.quick('isReady',function(){
sensors.getPresetProperties()
});
7、插件集成
(1)使用内置插件,
内置插件
Amp、SensorsChannel、Deeplink、PageLeave、PageLoad、RegisterPropertyPageHeight、SiteLinke
使用
sensors.use('PageLeave', option);
sensors.init({
...初始化参数
})
(2)使用外置插件,
外置插件
AesEncryption、Exposure、SessionEvent、SiteLinkerConcatUtm、WechatWebViewChannel、SensorsABTest、Popup(H5版)WebPopup(Web版)、GeneralEncryption、CustomEventsSender、SfInstantEvent、ChannelUtm、SMEncryption
使用
import sessionEvent from '/dist/web/plugin/session-event/index.es6.js';
sensors.use(sessionEvent);
sensors.init({
...初始化参数
})
8、多域名打通
(1)示例
sensors.init({});
sensors.use('SiteLinker',
{
linker: [ // 在神策分析环境中实现用户统一,从而实现跨域打通
{ part_url: 'sensorsdata.cn', after_hash: false },
{ part_url: 'example.com', after_hash: false }
],
re_login: true, //如果从已登录的页面跳转过来,即使当前网页已经登录,当前网页仍然会以之前网页的登录id再次登录
}
);
sensors.quick('autoTrack');
(2)打通结果
(3)原理说明
A、浏览器中的Cookie包含用户id和域名
B、多域名间共享用户id
C、只有a标签跳转可以实现跨域打通
(4)实施方案
A、linker参数的part_url属性配置‘需要打通的网域’
B、神策检查本网域中的a标签的链接,如果链接包含part_url,将从Cookie中提取本网域下的Distinct ID拼接到链接上
C、跳转到目标网域后,目标网域截取网址中的链接器_sasdk参数,用其替换自身的Distinct ID
D、反过来也一样
9、渠道追踪与广告(以下数据自动生成)
注意,从搜索引擎的搜索结果向目标网页跳转时,如果搜索引擎不携带相关参数,会导致下列数据的部分字段为空
(1)用户相关属性
(2)事件相关属性
(3)流量来源类型
(4)搜索引擎关键词
(5)场景示例
(6)常见问题
10、曝光采集,
包含名称、配置、属性
(1)初始化与注销
初始化
import Exposure from '/dist/web/plugin/exposure/index.es6';
var exposure = sensors.use(Exposure, {
area_rate: 0,
stay_duration: 2,
repeated: true
});
注销
exposure.removeExposureView(document.getElementById('exposure_ele'))
(2)注册曝光元素
方案1
<div
data-sensors-exposure-event-name="home_top_banner"
data-sensors-exposure-config-area_rate="1"
data-sensors-exposure-config-stay_duration="2"
data-sensors-exposure-config-repeated="true"
data-sensors-exposure-property-propA="valueA"
data-sensors-exposure-property-propB="valueB"
></div>
方案2
<div id="exposure_div"></div>
var exposureDiv = document.getElementById("exposure_div");
exposureDiv.setAttribute("data-sensors-exposure-event-name", "exposure_ele"); //1、名称
exposureDiv.setAttribute( //设置曝光元素配置及自定义属性
"data-sensors-exposure-option",
JSON.stringify({
config: { //2、配置
area_rate: 0.5,
stay_duration: 0,
repeated: true
},
properties: { //3、属性
d: "abc",
e: false
},
listener: { //4、曝光的回调
shouldExpose: function (ele, properties) { //是否触发曝光事件
// ele 为当前曝光的元素
// properties 为曝光元素的元素信息及该元素的曝光自定义属性
// 触发曝光事件则返回 true
// 不触发曝光事件则返回 false
return true;
},
didExpose: function (ele, properties) { //已触发曝光回调
// ele 为当前曝光的元素
// properties 为曝光元素的元素信息及该元素的曝光自定义属性
}
}
})
);
11、谷歌AMP
(1)说明,AMP提供了可分析用户行为数据的<amp-analytics>元素,通过该元素实现对神策埋点数据的采集
(2)集成,在head中引入拓展组件<amp-analytics>的脚本
<script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
(3)采集,在body中引入<amp-analytics>标签,标签type="sensorsanalytics",通过编辑标签内部的json配置,来进行行为数据采集
<amp-analytics type="sensorsanalytics" id="sensorsanalytics1">
<script type="application/json">
"requests": {
"event": "https://jssdkdata.debugbox.sensorsdata.cn/sa?project=liangshuang" //数据接收地址
},
"triggers": { //下面的key相当于上面的$WebClick、$WebStay、$pageview
"trackPageview": { //页面浏览事件采集
"on": "visible",
"request": "event",
"vars":{
"event":"amp_pageview", //自定义事件名称
"amp_properties":"%7B%22platform%22%3A%22amp%22%2C%22color%22%3A%22red%22%7D" //自定义属性
}
},
"id_test1": { //自定义事件采集
"on": "click",
"selector": "#test1", //点击事件需要监听的元素选择器
"request": "event",
"vars":{
"event":"click_test1", //自定义事件名称
"amp_properties":"%7B%22platform%22%3A%22amp%22%2C%22color%22%3A%22red%22%7D" //自定义属性
}
},
"class_test2": { //自定义事件采集
"on": "click",
"selector": ".test2", //点击事件需要监听的元素选择器
"request": "event",
"vars":{
"event":"click_test2", //自定义事件名称
"amp_properties":"%7B%22platform%22%3A%22amp%22%2C%22color%22%3A%22red%22%7D" //自定义属性
}
}
}
</script>
</amp-analytics>
(4)amp_properties值的生成步骤
var prop = {
platform:'amp',
color:'red'
}
var amp_properties = encodeURIComponent(JSON.stringify(prop))
(5)AMP网页的三种访问方式
AMP查看器访问
代理/缓存访问
普通浏览器直接访问
(6)AMP页面和非AMP页面的用户统一
sensors.init({});
sensors.use('Amp'); //引入AMP插件
sensors.quick('autoTrack');
(7)检测AMP集成是否成功
触发一条埋点事件,查看network是否有sa.gif的请求发出
12、深度链接deeplink
(1)需求,客户希望通过H5将用户引流至移动 App,借助于深度链接可以提高活动推广的效果
13、常见问题FAQ
附、常见问题解答(frequently-asked questions,简称FAQ)是使新用户熟悉规则的一种方法
(1)单页面的页面标题$title问题
router.beforeEach((to, from, next) => {
document.title = '新页面的 title 值';
next()
})
(2)异步集成时,使用“内置(但)未使用插件”
sensors.quick('isReady',function(){
sensors.use('PageLeave')
sensors.quick('autoTrack')
})
附、采集数据的含义
注释参考1;https://manual.sensorsdata.cn/sa/latest/zh_cn/%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F-149553285.html
注释参考2;https://manual.sensorsdata.cn/sa/latest/zh_cn/id-key-150668129.html
注释参考3;https://manual.sensorsdata.cn/sa/latest/zh_cn/%E9%A2%84%E7%BD%AE%E5%B1%9E%E6%80%A7%E6%80%BB%E8%A1%A8%E6%A0%BC-152305885.html
注释参考4;https://manual.sensorsdata.cn/sa/latest/zh_cn/kafka-154632771.html
[web-sdk-log]: {
"identities": {
//identities:全域用户关联业务中用户标识字段,可包含多个用户标识,具体可以参考全域用户关联,
//https://manual.sensorsdata.cn/sa/latest/zh_cn/%E6%A0%87%E8%AF%86%E7%94%A8%E6%88%B7%E2%80%94%E2%80%94%E5%85%A8%E5%9F%9F%E7%94%A8%E6%88%B7%E5%85%B3%E8%81%94-150667578.html
"$identity_cookie_id": "18dcb37ea531b29-088c1d7f7926fa8-1e525637-2104200-18dcb37ea54bd9"//Web cookie ID
},
"distinct_id": "18dcb37ea531b29-088c1d7f7926fa8-1e525637-2104200-18dcb37ea54bd9",//类型是字符串,对用户的标识,对未登录用户,可以填充设备标识、CookieID 等,对于登录用户,则应该填充注册账号;这里的例子,假设是一个匿名用户,所以填充的是一个设备 ID;
"lib": {//SDK
"$lib": "js",//SDK 类型
"$lib_method": "code",//埋点方式
"$lib_version": "1.26.5"//SDK 版本
},
"properties": {//属性
"$timezone_offset": -480,//时区偏移量
"$screen_height": 1169,//屏幕高度
"$screen_width": 1800,//屏幕宽度
"$viewport_height": 363,//视区高度
"$viewport_width": 1752,//视区宽度
"$lib": "js",//SDK 类型
"$lib_version": "1.26.5",//SDK 版本
"$referrer": "http://test.cctv.com/",//前向地址,上一页面的$url信息
"$url": "http://test.cctv.com/#/home",//页面地址
"$url_path": "/",//页面路径
"$title": "智策",//页面标题
"$latest_referrer": "取值异常",//最近一次站外地址
"$latest_search_keyword": "取值异常",//最近一次搜索引擎关键词
"$latest_traffic_source_type": "取值异常",//最近一次流量来源类型
"$is_first_day": false,//是否首日访问
"$is_first_time": false,//是否首次触发事件
"$referrer_host": "test.cctv.com",//前向域名
"tmzone": 8,//时区
"b": "Chrome",//浏览器名称
"o": "MacIntel",//操作系统名称
"lng": "zh-CN",//操作系统语言
"ism": "不是移动端",//移动系统
"param": "#/home",//页面URL #后部分
"logtype": 1,//1-访问日志,2-交互日志
"single_page": true,//是否单页面应用
"show_log": true,//控制台是否显示日志
"v_id": "121212"//(自定义字段)
},
"anonymous_id": "18dcb37ea531b29-088c1d7f7926fa8-1e525637-2104200-18dcb37ea54bd9",//login_id、anonymous_id:类型是字符串,对用户的标识,对未登录用户,只有 anonymous_id,而无 login_id 信息;
"type": "track",//表示一条数据的具体操作(小结部分会详细介绍),track 表明是记录一个事件
"event": "$pageview",//事件名,需是合法的变量名,即不能以数字开头,且只包含:大小写字母、数字、下划线和 $,其中以 $ 开头的表明是系统的预置事件,自定义事件名请不要以 $ 开头,且 event 字段长度最大为 100;//事件($WebClick、$WebStay、$pageview)
"time": 1709626986084,//类型是数值,事件发生的实际时间戳,精确到毫秒;
"_track_id": 319126084,//前端 SDK track 的时候上报的随机值,用于去重判断,并不会写入 events 表
"_flush_time": 1709626986084//发送数据时的时间
}
十七、神策源码解读
注意,先做项目(了解源码的功能),再看源码(了解功能的实现)
注意,神策数据分为神策采集和神策分析,sd.use、sd.init、页面事件,三者一起添加监听事件
来源,https://juejin.cn/post/6974267640797724679
1、客户自有数据有三类
(1)前端操作,通过插件sensorsdata.full.js,采集、发送数据,该插件适用于PC、安卓、苹果、小程序等一切前端
(2)后端日志
(3)业务数据
2、插件采集数据的方式
(1)代码埋点
(2)全埋点
(3)可视化全埋点
(4)预置属性
3、在项目中使用
(1)在项目中引入采集插件,即1万多行的sensorsdata.full.js
(2)在项目中插入采集代码,把采集数据发送给神策数据服务器
(3)用神策数据的账号、密码登录神策数据网站,查看采集数据
4、存储方式
(1)cookie
(2)localStorage
(3)sessionStorage
5、发送方式
(1)ajax,通过ajax异步POST请求将数据发送出去
(2)image,图片发送也是默认的发送方式,创建一个img标签,将数据放在请求的URL中,以问号传参的方式发送
(3)sendBeacon,浏览器会取消unload事件里的卸载、刷新、跳转逻辑,发出请求可以通过navigator.sendBeacon(导航员.发送信标)或同步ajax
6、全局变量
var sd = {}; //1,sensors-data
var ee = {}; //6711,event-emit
var sdk = new EventEmitter(); //6710,sensors-data-kit
var _ = extend({}, W, business); //7545
7、ee,与事件相关,
//以下在全局添加属性
ee.spa = spa;
ee.initSystemEvent
ee.EVENT_LIST
ee.sdk = sdk; //6715
ee.sdk.on,监听事件
ee.sdk.emit,触发事件
8、sd,与框架相关
//以下在implementCore里添加属性,
sd._ = _;
sd.ee = ee;
sd.on = eventEmitterFacade;//调用时,最终调用ee.sdk.on
sd.use = use;
sd.readyState = readyState;
//以下在use里添加属性
sd.modules = sd.modules || {}; //存放插件
//以下暴露自身,并执行sd.init,同时注意区分变量和字符串
window[sensorsDataAnalytic201505] = sd;
window['sensorsDataAnalytic201505'] = sd;//11415
sd.init();
9、真实项目中的配置,默认自动开启日志输出功能方便调试,
来源,https://manual.sensorsdata.cn/sa/latest/tech_sdk_client_web_policy-109576387.html
<script>
window.sensors_data_pre_config = {
is_compliance_enabled: true //是否同意启用
}
</script>
<script charset='UTF-8' src="./sensorsdata.min.js"></script> //sd.init,在插件同步加载结束时,不传参执行1次
<script>
if(isClientAgree){//同意隐私条款
sensors.init({ //sd.init,启用插件时,传参执行1次
server_url: 'http://test-syg.datasink.sensorsdata.cn/sa?token=xxxxx&project=xxxxxx',
is_track_single_page: true, //单页面配置,默认开启,若页面中有锚点设计,需要将该配置删除,否则触发锚点会多触发$pageview事件
use_client_time: true,
send_type: 'beacon',
heatmap: {
//是否开启点击图,'default'表示开启,自动采集 $WebClick 事件,可以设置'not_collect'表示关闭
clickmap: 'default',
//是否开启触达图,'not_collect'表示关闭,不会自动采集 $WebStay 事件,可以设置'default'表示开启
scroll_notice_map: 'not_collect'
}
});
sensors.quick('autoTrack');
}
</script>
10、页面监听与发射
附、sd.init = function(para) {
ee.initSystemEvent();
sd.detectMode();
};
(1)监听
A、sd.init
a、sd.detectMode();
B、function detectMode() {//检测模式
a、trackMode();
C、function trackMode() {//跟踪模式
a、listenSinglePage();
D、function listenSinglePage(trackFn) {//监听单个页面
if (sd.para.is_track_single_page) {
spa.on('switch', function(last_url) {//监听(后出现,立即执行)
var sendData = function(extraData) {
extraData = extraData || {};
if (last_url !== location.href) {//浏览器前进或后退(用户触发)
pageInfo.pageProp.referrer = getURL(last_url);
var data = extend({
$url: getURL(),
$referrer: getURL(last_url)
}, extraData);
isFunction(trackFn) ? trackFn(data) : sd.quick && sd.quick('autoTrack', data);//执行quick
}
};
if (typeof sd.para.is_track_single_page === 'boolean') {
sendData();
} else if (typeof sd.para.is_track_single_page === 'function') {
var returnValue = sd.para.is_track_single_page();
if (isObject(returnValue)) {
sendData(returnValue);
} else if (returnValue === true) {
sendData();
}
}
});
}
}
E、function quick() {
a、commonWays[arg0].apply(commonWays, arg1);//执行autoTrack
F、autoTrack: function(para, callback) {//自动跟踪
a、sd.track(
G、function track(
a、saEvent.send({
H、send: function() {
I、request: function(data, dataKeys) {
J、function ajax$1(para) {
K、function ajax(para) {
(2)发射
A、sd.init
a、ee.initSystemEvent();
B、ee.initSystemEvent = function() {
addSinglePageEvent(function(url) {
spa.emit('switch', url);//发射(先出现,不立即执行)
});
};
C、function addSinglePageEvent(callback) {
var current_url = location.href;
//以下重定义方法,两种操作时执行,执行发射逻辑
var historyPushState = window.history.pushState;
var historyReplaceState = window.history.replaceState;
if (isFunction(window.history.pushState)) {
window.history.pushState = function() {//浏览器前进或后退(用户触发)
historyPushState.apply(window.history, arguments);
callback(current_url);//执行spa.emit('switch',但不出现
current_url = location.href;
};
}
if (isFunction(window.history.replaceState)) {
window.history.replaceState = function() {
historyReplaceState.apply(window.history, arguments);
callback(current_url);
current_url = location.href;
};
}
//以下添加事件,五种操作时执行,执行发射逻辑
var singlePageEvent;
if (window.document.documentMode) {//IE浏览器
singlePageEvent = 'hashchange';
} else {//非IE浏览器
singlePageEvent = historyPushState ? 'popstate' : 'hashchange'; // html5 : html5以下
}
addEvent(window, singlePageEvent, function() {
callback(current_url);//执行spa.emit('switch',但不出现
current_url = location.href;
});
// (6)两种操作的作用,给“浏览器当前页签”添加(或修改)状态,以下是两种操作,
// 附、它们是HTML5的一个API
// A、pushState
// B、replaceState
// (7)五种操作的作用,给“浏览器当前页签”的“不同历史记录”激活,以下是五种操作
// A、浏览器的后退(向左)
// B、浏览器的前进(向右)
// C、history.back(向左)
// D、history.forward(向右)
// E、history.go(负左,正右,0当前)
// (8)两种操作(手动)、五种操作(手动)、onpopstate(自动),三者之间的关系
// A、两种操作,只能改变当前页面状态,不会触发window.onpopstate(event)事件
// B、五种操作,与两种操作相关与否,都会触发window.onpopstate(event)事件
// C、五种操作,与两种操作无关时,还会触发http请求
}
D、function addEvent(target, eventName, eventHandler, useCapture) {
var register_event = function(element, type, handler) {
if (useCapture === undefined && type === 'click') {
useCapture = true;
}
if (element && element.addEventListener) {
element.addEventListener(//被五种操作中的一种操作触发
type,//popstate
function(e) {
e._getPath = fixEvent._getPath;
handler.call(this, e);
},
useCapture
);
} else {
var ontype = 'on' + type;
var old_handler = element[ontype];
element[ontype] = makeHandler(element, handler, old_handler, type);
}
};
register_event.apply(null, arguments);
}
(3)ajax的使用
附、定义,function ajax(para) {
A、function ajax$1(para) {
return ajax(para);
}
a、AjaxSend.prototype.start = function() {
b、request: function(data, dataKeys) {
c、var business = {
B、var SADeepLink = {
init: function init(sd) {
this.sd._.ajax({
},
};
a、var index$5 = createPlugin$5(SADeepLink, 'Deeplink', 'sdkReady');
C、function debugPath(data) {
_$6.ajax({});
}
a、debugPath(JSON.stringify(data));
附、hashchange,该事件在当前URL的锚部分发生修改时触发。它在本插件的应用可能如下
a、监听hashchange事件
b、当url上的hash改变时,触发hashchange事件,向后台发送相关数据
11、源码摘录
(1)extend,initPara--extend--each,外部数组遍历用forEach,内部对象遍历用for in,后面属性覆盖前面属性
var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
var hasOwnProperty$2 = Object.prototype.hasOwnProperty;
function initPara(para) {
extend(sdPara, para || sd.para || {});
sd.para = sdPara;
}
function extend(obj) {
each(Array.prototype.slice.call(arguments, 1), function(source) {
for (var prop in source) {
if (hasOwnProperty$1.call(source, prop) && source[prop] !== void 0) {
obj[prop] = source[prop];
}
}
});
return obj;
}
function each(obj, iterator, context) {
if (obj == null) {
return false;
}
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);//在函数的定义处执行
} else if (isArray(obj)) {
for (var i = 0, l = obj.length; i < l; i++) {
i in obj && iterator.call(context, obj[i], i, obj); // iterator定义里只有一个参数,这里传进去三个参数,在不改变iterator定义的情况下,第二、三个参数是无用的
}
} else {
for (var key in obj) {
if (hasOwnProperty$2.call(obj, key)) {
iterator.call(context, obj[key], key, obj);
}
}
}
}
(2)与use相关
//溯源插件,index$d-createPlugin$d-wrapPluginInitFn$d(重写init)
//执行插件,use(Plugin)->Plugin.init()-sd.on->eventEmitterFacade->ee[splitEvent[0]].on(splitEvent[1],callback)->ee.EVENT_LIST
//注册拦截器,registerInterceptor
//用2个for循环对2组插件进行遍历,并将每个插件放进sd.modules中
var builtinPlugins = [index$1, index$2, index$3, index$4, index$5, index$6, index$7, index$8, index$9, index$a,
index$b, index$c, index$d, index$e, index$f, index$g, index$h, index$i, index$j, index$k]; // 共20项
var autoUsePlugins = [index, index$d, index$e, index$g, index$f, index$2, index$6, index$3, index$7, index$h,
index$i, index$j, index$k]; // 共13项
for (var i = 0; i < builtinPlugins.length; i++) {
var p = builtinPlugins[i];
if (sd._.isString(p.plugin_name)) {
sd.modules[p.plugin_name] = p; //不需要执行plugin.init,把插件存入sd.modules
} else {
sd._.isArray(p.plugin_name) &&
sd._.each(p.plugin_name, function(v) {
sd.modules[v] = p;
});
}
}
for (i = 0; i < autoUsePlugins.length; i++) {
sd.use(autoUsePlugins[i]); //需要执行plugin.init,把插件存入sd.modules
}
function use(plugin, option) {
function initPlugin() {
!curPlugin.plugin_is_init && curPlugin.init(sd, option);
curPlugin.plugin_is_init = true;
sd.modules = sd.modules || {};
sd.modules[curPlugin.plugin_name || 'unnamed_' + nonameCount++] = curPlugin; //把插件存入sd.modules
return curPlugin;
}
return initPlugin();
}
var index$d = createPlugin$d(utm, 'Utm', 'sdkAfterInitPara'); //utm是plugin的实参
function createPlugin$d(plugin, name, lifeCycle) { //添加版本
wrapPluginInitFn$d(plugin, name, lifeCycle);
plugin.plugin_version = sdkversion_placeholder$e;
return plugin;
}
function wrapPluginInitFn$d(plugin, name, lifeCycle) { //重写init
if (name) {
plugin.plugin_name = name;
}
if (lifeCycle && plugin.init) {
var initFn = plugin.init; //存储旧的
plugin.init = function(sd, option) { //赋值新的
if ((sd.readyState && sd.readyState.state >= 3) || !sd.on) {
return initPlugin();
}
sd.on(lifeCycle, initPlugin);
function initPlugin() {
initFn.call(plugin, sd, option);
}
};
}
return plugin;
}
(3)与sd.init相关
sd.init = function(para) { //8669
ee.sdk.emit('beforeInit');
if (sd.readyState && sd.readyState.state && sd.readyState.state >= 2) {
return false;
}
if (is_compliance_enabled) { //是否同意启用
implementCore(true); //给sd添加属性
checkState(); //给sd的方法重新定义
}
ee.initSystemEvent(); //初始化系统事件
sd.setInitVar(); //设置初始化变量
sd.readyState.setState(2); //设置全局状态
sd.initPara(para); //约130行,暂未了解
ee.sdk.emit('initPara');
ee.sdk.emit('afterInitPara');
ee.sdk.emit('initAPI');
ee.sdk.emit('afterInitAPI');
sd.detectMode(); //检测模式,暂未了解
iOSWebClickPolyfill(); //IOS系统兼容处理-可能
ee.sdk.emit('afterInit');
ee.sdk.emit('ready');
};
function implementCore(isRealImp) {
if (isRealImp) {
sd.events = events;
sd.bridge = bridge;
sd.SDKJSBridge = SDKJSBridge;
sd.JSBridge = DeprecatedJSBridge;
sd.store = store;
sd.unlimitedDiv = unlimitedDiv;
sd.customProp = customProp;
sd.vtrackcollect = vtrackcollect;
sd.vapph5collect = vapph5collect;
sd.detectMode = detectMode;
sd.registerFeature = registerFeature;
sd.registerInterceptor = registerInterceptor;
sd.commonWays = commonWays;
registerFeature(new CoreFeature(sd));
registerFeature(new HeatCollectFeature(sd));
registerInterceptor('viewStage', heatCollectInterceptor);
}
var imp = isRealImp ? functions : saEmpty;
for (var f in imp) {
sd[f] = imp[f];
}
sd._ = _;
sd.on = eventEmitterFacade;
sd.ee = ee;
sd.use = use;
sd.lib_version = sdkversion_placeholder;
}
function checkState() {
each(methods, function(method) {
var oldFunc = sd[method];
sd[method] = function() {
if (sd.readyState.state < 3) {
if (!isArray(sd._q)) {
sd._q = [];
}
sd._q.push([method, arguments]);
return false;
}
return oldFunc.apply(sd, arguments);
};
});
}
ee.initSystemEvent = function() {
addSinglePageEvent(function(url) {
spa.emit('switch', url);
});
};
十八、aplus.js
附、meta标签
<html>
<head>
<meta charset="UTF-8">
<meta name="description" content="免费的 Web 教程">
<meta name="keywords" content="HTML,CSS,JavaScript">
<meta name="author" content="YK Investment">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="my-name" content="name的等号后面是属性值,由开发者确定">
<meta name="your-name" content="特定的属性值如keywords、viewport">
<meta name="your-name" content="会被网络爬虫或浏览器识别!">
</head>
</html>
<script>
//name的等号后面是属性值,由开发者确定,特定的属性值如keywords、viewport,会被网络爬虫或浏览器识别!
//js获取meta某name的content
//方法1
var metas = document.getElementsByTagName('meta');
for (var i = 0; i < metas.length; i++) {
if (metas[i].getAttribute('name') === 'my-name') {
console.log(metas[i].getAttribute('content'));
}
}
//方法2
var names = document.getElementsByName('your-name');
for (var i = 0; i < names.length; i++) {
console.log(names[i].getAttribute('content'));
}
</script>
附、通用术语
(1)PV,页面浏览量或点击量,page view
(2)UV,独立访客数,unique visitor
(3)QPS,每秒查询率,Query Per Second
(4)TPS,每秒吞吐量,Throughput Per Second,系统在单位时间内处理请求的数量
(5)RT,响应时间,Response Time
1、aplus.js说明
(1)源码地址,https://js.data.cctv.com/__aplus_plugin_cctv.js,aplus_plugin_aplus_u.js
(2)使用方法,https://help.aliyun.com/product/194063.html,费了好大劲才找到
(3)二次封装,__aplus_plugin_cctv.js和aplus_plugin_aplus_u.js,是领导给我的
(4)相关域名
A、收数,https://log-api.aplus.emas-poc.com/,aplus.js的服务器
B、友盟,https://developer.umeng.com/docs/67963/detail/74526
C、分享,https://blog.naaln.com/2017/08/alibaba-data-track-1/
D、术语,https://zhuanlan.zhihu.com/p/650155427
2、aplus.js术语
来源,https://blog.csdn.net/qq_38397338/article/details/125246947
(1)SPM,全称超级位置模型,Super Position Model,记录和描述用户的具体点击位置信息,如外站类型、应用id、频道id、页面id
A、spm-cnt,当前页面的SPM编码(spm_id)
B、spm-url,当前页面的来源位置的SPM编码(url_spm_id)
C、spm-pre,来源页面的来源位置的SPM编码(pre_spm_id)
D、由a.b.c.d四段构成,各分段分别代表
a:站点/业务
b:页面
c:页面区块
d:区块内点
其中a,b位是必须具备的,且均需在SPM申请中心,如a1.b1.c1.d1
(2)SCM,全称超级内容模型,Super Content Model
(3)Quick Tracking,快速追踪(全域采集与增长分析),阿里云推出的(基于aplus.js的)企业级流量统计分析产品
(4)黄金令箭,用户按照约定的格式向日志服务器发送请求
A、系统自动采集的页面浏览(PV, Page View)日志
B、自定义日志(时间内容自定义),通过调用黄金令箭日志接口主动上报日志
(5)aplus-auto-exp,曝光时,aplus自动发送黄金令箭
(6)aplus-auto-clk,点击时,aplus自动发送黄金令箭
(7)自定义属性,data-spm、data-spmA、data-spmB
(8)自动点击,spmC、spmD
3、aplus.js采集内容包括
(1)ID,cookieID、淘宝会员数字id(不一定需要登录)、nickname(若已登录)
(2)URL,当前页URL、来源页URL
(3)SPM,当前页SPM、来源页点击位置SPM、来源页的来源页点击位置SPM编码(前提为,已部署了SPM)
(4)信息,标题、浏览器名称版本、屏幕分辨率、aplus版本
(5)反作弊验证码
附、交互行为使用「黄金令箭」统计
4、aplus.js自动埋点,当某个元素出现在可视区域,或者点击某个元素的时候,aplus会自动发送黄金令箭
来源,https://blog.csdn.net/qq_38397338/article/details/125246947
(1)html埋点,
A、标识站点ID,<meta name="data-spm" content="申请的A位编码">
B、标识页面ID,<body data-spm="注册的B位编码"></body>
C、标识容器ID,<div data-spm="自定义或注册的C位编码"></div>
D、标识链接ID,<a data-spm="d开头的自定义编码串" href=""></a>
E、html埋点,<meta name="aplus-auto-exp"
content='[{"logkey":"/abc1","tag":"div","filter":"data-name","props":["name","age","address"]}]'>
当带有data-name属性的div标签被曝光时,自动采集这个标签的曝光信息,及当前元素的"name",“age”,"address"这几个属性
(2)js埋点
A、示例
handleClick(clkType) {
var q = (window.goldlog_queue || (window.goldlog_queue = []));
q.push({
action: 'aplus.record', //发送日志请求。放在这里调用,是为了避免aplus未完成初始化
arguments: [
'/aplus.99.3',//(埋点方案中的事件编码)
'CLK',
'clickType='+clkType,
'POST'
]
});
q.push({
action: 'aplus.setMetaInfo', //识别需要曝光的元素
arguments: ['aplus-auto-exp',
[
{ //aplus自动曝光,发送黄金令箭
cssSelector: '.auto-exp-component', //需要曝光的元素class
positionSelector: '.parent',//如果页面模块是元素内滚动(某个区块内有滚动条)则需要增加positionSelector辅助定位曝光元素
logkey: 'test_event_id', //事件管理中的事件id
props: ['data-itemid'], //你要曝光的元素身上自定义属性
},
...
], ],
});
q.push({
action: 'aplus.setMetaInfo',
arguments: ['aplus-auto-exp',
JSON.stringify([
{
"logkey": "/abc1",
"tag": "div",
"filter": "data-name",
"props": ["name", "age", "address"]
}
])
]
});
q.push({
'action':'aplus.sendPV',
'arguments':[{
is_auto: false
}, {
page_title: "首页", //默认为pageConfig中的值,如果这里设置了,则为这里设置的值 (非必传)
page_name: "yourCurrentPageName", //默认为pageConfig中的值,如果这里设置了,则为这里设置的值 (非必传)
//如果您设置了duration参数(单位须为毫秒),QuickTracking会做为分析时的「事件属性-时长(s)」处理
duration: 1111111,
//自定义事件属性
x: 111,
y: 222
}]
});
}
<button onClick={this.handleClick('one')}>HJLJ ONE</button>
B、参数说明,goldlog.record(logkey, gmkey, gokey, req_method)
logkey{String},即完整的令箭编码,如上例中的 “/a1.jingyao.clicktest”
gmkey{String},关键业务类型,目前的约定的元值有五个,
点击类操作"CLK"、
曝光类事件"EXP"、
滑屏类事件"SLD"、
其它事件"OTHER"(特指除点击和曝光事件外的其他自定义事件)、
也可以为空值,但不建议留空
gokey{String},附加的自定义kv对,如本例中clicktype=one
req_method{String},可选值有’GET’(默认)、‘POST’
如果入参为POST,则令箭请求会优先navigator.sendBeacon post的形式发出,可以保证页面跳转的时候不会断
如果当前浏览器不支持sendBeacon则降级为get img的形式发出
5、Quick Tracking的基本概念,全域采集与增长分析-产品概述
来源,https://help.aliyun.com/document_detail/250932.html
(1)行为采集
A、系统事件(APP有,小程序有,Web无),应用启动($$_app_start)、应用退出($$_app_end)、分享($$_share)
B、页面事件,采集页面浏览行为的事件,包含页面事件标识码和页面编码
C、自定义事件,
D、属性,包含全局属性和事件属性
(2)用户标识,包含设备和用户
6、Web可视化分析使用文档-集成,全域采集与增长分析-操作指南-采集管理-可视化埋点
附、相关概念
A、可视化分析,利用图形、图像、动画等直观方式来展现数据的分析方法
B、可视化埋点,用于分析用户在应用程序或网站上的行为和交互的技术
来源,https://help.aliyun.com/document_detail/281212.html
(1)第一步:确认SDK支持投屏
注、支持投屏,意味着一个设备能够将它的画面复制并显示在另一个设备上
A、用户将发射端SDK集成到自己的APP或软件应用中
B、配套投屏接收端设备
(2)第二步:确认SDK已经集成
A、首先,将以下脚本放在head标签内
<script>
(function(w, d, s, q) {
w[q] = w[q] || [];
var f = d.getElementsByTagName(s)[0],j = d.createElement(s);
j.async = true;
j.id = 'beacon-aplus';
j.src = '<sdk地址>';
f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'aplus_queue');
</script>
B、其次,配置必要的meta
<html>
<head>
<meta name="appKey" content="<QuickA+申请应用时颁发的appKey>">
<meta name="aplus-rhost-v" content="<日志收数域名>">
<meta name="aplus-vt-cfg-url" content="<已发布的配置地址>">
</head>
</html>
C、说明
appKey:QuickA+申请应用时颁发的appKey
aplus-rhost-v:日志收数域名
aplus-vt-cfg-url:已发布的配置地址
(3)第三步:配置SPM
注、必须使用自动点击、自动曝光上报数据
A、配置页面级别spm(spmB)
第一种:通过给body标签添加自定义属性data-pagename
<body data-pagename=${yourPageName}></body>
第二种:手动调用API设置spmB
aplus_queue.push({
action: 'aplus.setPageName',
arguments: [${yourPageName}]
});
B、自动点击配置spm(spmC、spmD)
注、通过每一自动点击配置项的spmC和spmD属性来配置spm
aplus_queue.push({
action:'aplus.setMetaInfo',
arguments:['aplus-auto-clk',[{
cssSelector:'.header',
logkey:'banner-clk',
spmC:"header",
spmD:"banner"
},{
cssSelector:'.component-category-industry',
logkey:'category-clk',
props:['categorytype','title'],
spmC:"component-category",
spmD:"industry"
},{
cssSelector:'.component-category-common',
logkey:'category-clk',
props:['categorytype','title'],
spmC:"component-category",
spmD:"business"
}]]
});
C、自动曝光配置spm(spmC、spmD)
注、通过每一自动曝光配置项的spmC和spmD属性来配置spm
aplus_queue.push({
action:'aplus.setMetaInfo',
arguments:['aplus-auto-exp',[{
cssSelector:'.header',
logkey:'banner-exp',
spmC:"header",
spmD:"banner"
},{
cssSelector:'.component-category-industry',
logkey:'category-exp',
props:['categorytype','title'],
spmC:"component-category",
spmD:"industry"
},{
cssSelector:'.component-category-common',
logkey:'category-exp',
props:['categorytype','title'],
spmC:"component-category",
spmD:"business"
}]]
});
D、自定义属性配置spm(spmC、spmD)
<body data-pagename=${yourPageName}>
<div data-spmc="c111">
<a href="链接1" data-spmd="d1" />
<a href="链接2" data-spmd="d2" />
</div>
<div data-spmc="c222">
<a href="链接3" data-spmd="d1" />
<a href="链接4" data-spmd="d2" />
</div>
</body>
data-pagename:spmB
data-spmc:spmC
data-spmd:spmD
7、引入&初始化SDK,全域采集与增长分析-开发参考-SDK参考-Web SDK
来源,https://help.aliyun.com/document_detail/473456.html
(1)参数准备
A、appkey:在应用列表中获取
B、收数域名:在“管理控制台-采集信息”模块中获取
C、SDK链接:在“管理控制台-采集信息”模块中获取
(2)SDK引入&初始化
A、当您得到集成SDK代码地址以后,在页面head标签内加入集成代码,确保aplus_queue不被污染
(function(w, d, s, q) {
w[q] = w[q] || [];
var f = d.getElementsByTagName(s)[0], j=d.createElement(s);
j.async = true;
j.id = 'beacon-aplus';
j.src = 'SDK链接';
f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'aplus_queue');
B、设置域名和appkey,以下代码紧跟SDK引入代码
//集成应用的appKey
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['appKey', '您的appkey']
});
//如果是私有云部署还需要在上面那段JS后面紧接着添加日志域名埋点
//通常私有云日志服务端域名类似于:xxx-web-api.xxx.com.cn, 具体域名在“管理控制台-采集信息”模块中获取
//2.x版本SDK设置收数域名
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['trackDomain', '您的收数域名']
});
//1.x版本SDK设置收数域名
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['aplus-rhost-v', '您的收数域名']
});
8、基础功能,全域采集与增长分析-开发参考-SDK参考-Web SDK
来源,https://help.aliyun.com/document_detail/602428.html
(1)原理
A、SDK 提供一种指令形态的埋点调用方式,您通过对 aplus 环境变量的指令队列 aplus_queue发送指令,
B、由 aplus 环境变量来执行指令,进而完成您的需求,:
C、指令格式如下
aplus_queue.push({
'action': "$APIName",
'arguments': [$arguments]//arguments为指定API的入参,
})
(2)action 参数代表发送指令的 API 名称,其入参为一个字符串,取值为枚举值,可用的枚举值如下
A、setMetaInfo:覆盖SDK的已有默认设置
B、appendMetaInfo: 追加SDK的默认配置
C、getMetaInfo:获取SDK的当前配置
D、record:发送事件日志
E、sendPV:发送页面日志
(3)arguments参数,为action中指定API的入参,格式是一个数组,数组内的元素顺序与API定义的入参顺序一致
(4)示例
A、变更SDK的默认设置
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: [metaName, metaValue]
});
B、获取SDK的当前配置
aplus.getMetaInfo(metaName);
C、发送事件日志
aplus_queue.push({
action: 'aplus.record',
arguments: [
trackerEventCode, //(埋点方案中的事件编码)
eventType,
eventParams
]
});
D、发送页面日志
aplus_queue.push({
action: 'aplus.sendPV',
arguments: [pageEventConfig, userData]
});
(5)日志打印
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['DEBUG', true]
});
(6)应用基础信息配置
在SDK引入部分,可以修改或者追加一些默认设置
//集成应用的appKey
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['appKey', 'xxxxxxx']
})
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['aplus-rhost-v', 'quickaplus-Web-api.xxx.com.cn']
});
//开启调试模式
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['DEBUG', true]
});
9、埋点API,全域采集与增长分析-开发参考-SDK参考-Web SDK
来源,https://help.aliyun.com/document_detail/602417.html
(1)设备ID设置
A、自动生成:默认逻辑,网站的设备ID只有浏览器发生变化或用户主动清除cookie和缓存时,设备ID会发生改变
B、手动上传:上传方式为赋值给"_dev_id",上传的长度要在24-36字符
C、示例
a、如采集用户ID是异步行为,需要先阻止SDK上报,设置BLOCK埋点
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_hold', 'BLOCK']
});
b、设置_dev_id
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_dev_id', '自定义设备ID']
});
c、因为采集用户ID是异步行为,故需要先设置BLOCK,再设置START
设置_hold=START后,事先被block住的日志会携带上用户信息逐条发出
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_hold', 'START']
});
(2)账号ID设置
A、在用户登录时,以及登录态进入H5时,都需要设置账号ID
B、因为设置后的每一条日志都将携带账号ID,但退出H5再进入后触发的事件不会携带账号ID
C、所以需要在用户登录时,以及登录态进入H5时设置账号ID
D、示例
a、用户登录时,获取到用户登录账号信息 or 用户已登录,通过cookie或者localstorage获取用户登录账号
function demoLogin() {
/*************************如果同步场景***********************************/
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_user_id', '用户的账号id']
});
/******************如果是异步场景,并且日志必须依赖用户账号***********************/
//先通过设置_hold=BLOCK阻塞采集上报
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_hold', 'BLOCK']
});
...
function callback() {
//获取异步回调结果中的用户账号id
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_user_id', '用户的账号id']
});
//再通过设置_hold=START允许采集上报
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_hold', 'START']
});
};
...
};
b、用户登出时,重置用户账号id
function demoLogOff() {
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_user_id', '']
});
};
(3)设备ID和账号ID获取
A、设备ID的获取
a、SDK自动生成的设备ID,获取方式如下:
在当前域名的cookie下存储名为cna的字段,可以通过解析document.cookie获取
b、通过_dev_id方式自定义上传的设备ID,获取方式如下:
开发者通过setMetaInfo设置_dev_id可以自定义设备ID,可通过aplus.getMetaInfo('_dev_id')读取
B、账号ID的获取
a、开发者通过setMetaInfo设置_user_id可以自定义用户账号ID,可通过aplus.getMetaInfo('_user_id')获取
(4)设置用户属性
A、通过预制事件编码 $$_user_profile 上报用户属性,事件类型为其他事件
B、在上报用户属性之前,需要先设置_user_id上报用户账号
C、示例
aplus_queue.push({
'action':'aplus.record',
'arguments':[
'$$_user_profile(埋点方案中的事件编码)',
'OTHER', //特指除点击和曝光事件外的其他自定义事件
{ //此外内容不可改变
name: 'sss', //用户属性1
gender: 'male', //用户属性2
class: '3', //用户属性3
}
]
});
10、常见场景与埋点建议,全域采集与增长分析-开发参考-SDK参考-Web SDK
来源,https://help.aliyun.com/document_detail/603464.html
(1)页面跳转前的事件发送
A、当用户在网页「点击href属性www.xxxx.com的a标签」时,触发的点击事件可能会因为页面立刻跳转而未发送出去,
B、若希望该场景下尽量保证数据的发送,可以进行页面延迟跳转,
C、事例代码如下
//点击链接
function targetLinkCLK(url) {
// 延迟页面跳转,给SDK预留发数时间
setTimeout(function(){
window.location.href = url;
}, 500);
aplus_queue.push({
action: 'aplus.record',
arguments: [
'track_alink_clk', //(埋点方案中的事件编码)
'CLK',
{
param1: xxxx,
param2: xxxx
}
]
});
}
十九、aplus.js源码阅读
1、外框
! function(e) { //e,总参数
function t(a) { //公函数,
if (o[a]) return o[a].exports;
var n = o[a] = {
exports: {},
id: a,
loaded: !1
};
return e[a].call(n.exports, n, n.exports, t), n.loaded = !0, n.exports //公函数执行,参数的某项执行
}
var o = {}; //总数据
return t.m = e, t.c = o, t.p = "", t(0) //公函数执行
}([function(e, t, o) { //对象,属性,公函数。公函数执行,参数的某项执行
"use strict";
! function() {
var e = window.goldlog || (window.goldlog = {});
e._aplus_plugin_cctv || (
e._aplus_plugin_cctv = {
status: "complete"
}, o(1).run()
)
}()
}, function(e, t, o) {
"use strict";
function a() {
var e = l.getCookie("userSeqId");
if (e) {
var t = document.getElementById("tb-beacon-aplus") || document.getElementById("beacon-aplus");
if (t) {
var o = t.getAttribute("exparams"),
a = "uidaplus=" + e;
o = o ? o.replace(/&aplus&/, "&" + a + "&aplus&") : a + "&aplus&sidx=aplusSidex", t.setAttribute("exparams", o)
}
}
return e
}
function n() {
var e = {};
try {
var t = goldlog.getMetaInfo("aplus-rhost-g-map");
"string" == typeof t ? e = JSON.parse(t) : "object" == typeof t && (e = t)
} catch (t) {
e = {}
}
return e
}
function r(e, t) {
var o = n();
return o && o[t] ? "//" + o[t] + t : e
}
var s = o(2),
l = o(3);
t.run = function() {}
}]);
2、功能扩展-单页面重新获取信息
来源,https://help.aliyun.com/document_detail/473456.html?spm=a2c4g.602417.0.0.b24b256fiPDTu3
(1)当您得到集成SDK代码-地址-以后,在页面head标签内加入集成代码,确保aplus_queue不被污染
(function(w, d, s, q) {
w[q] =w[q] || [];
var f=d.getElementsByTagName(s)[0],j=d.createElement(s);
j.async=true;
j.id='beacon-aplus';
j.src='SDK链接';
f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'aplus_queue');
//扩充功能-应该-写在这里
(2)扩充功能
function pushData(){
// var aaa = window.goldlog.getMetaInfo("aplus-auto-exp")
window.goldlog_queue.push({
action: 'aplus.sendPV',//发送页面日志
arguments:[
{
is_auto: true
}, {
page_title: "首页", //默认为pageConfig中的值,如果这里设置了,则为这里设置的值 (非必传)
page_name: "yourCurrentPageName", //默认为pageConfig中的值,如果这里设置了,则为这里设置的值 (非必传)
//如果您设置了duration参数(单位须为毫秒),QuickTracking会做为分析时的「事件属性-时长(s)」处理
duration: 1000000000,
//自定义事件属性
x: 111,
y: 222
}
]
});
}
function isFunction(myFunction){
return Object.prototype.toString.call(myFunction) === "[object Function]"
}
function addEveryPageEvent() {
var historyPushState = window.history.pushState;
var historyReplaceState = window.history.replaceState;
if (isFunction(window.history.pushState)) {
window.history.pushState = function() {
console.log( 'pushState' );
historyPushState.apply(window.history, arguments);
pushData()
};
}
if (isFunction(window.history.replaceState)) {
window.history.replaceState = function() {
console.log( 'replaceState' );
historyReplaceState.apply(window.history, arguments);
pushData()
};
}
if (window.addEventListener) {
window.addEventListener('popstate', function(){
console.log( 'popstate' );
pushData()
}, false);
}
}
addEveryPageEvent()
(3)扩充功能不应该写在这里
e.init = function() {
i.initLoad.init_watchGoldlogQueue("metaQueue"), n(90)(function() {
})
//扩充功能-不应该-写在这里
}
3、使用说明
来源,https://help.aliyun.com/document_detail/473456.html?spm=a2c4g.602417.0.0.b24b256fiPDTu3
(1)在QuickTracking后台,为每一个Web应用生成了专属的集成代码,可以根据产品内的引导进行集成
(2)参数准备
A、appkey,在应用列表中获取
B、收数域名,在“管理控制台-采集信息”模块中获取
C、SDK链接,在“管理控制台-采集信息”模块中获取