小前端的项目优化之如何干到用golang手薅打包服务

背景🚗🚕🚙

接手了一个前端项目,是用的nunjucks模版来做的,其余技术皆为纯原生。

项目遇到的问题

纯原生的项目虽然可扩展性极高,但由于之前公司对于web的网页并没有要求很高的性能,我的前辈大哥在构建项目的时候并没有对性能方面做太多考虑,这个项目要求必须有很高的模块可操作性,所以选用nunjucks模版来进行模块化渲染html,再加上此页面要用上很多的服务端数据计算来生成,所以就导致了两个问题👉👉👉:一个页面的加载要请求至少七个html文件;一个页面的生成至少调用8个左右的js文件进行计算。

为什么不用webpack等前端打包工具

当然是尝试使用过了,但在打包到js用nunjucks模版加载html文件的时候失败了,尝试了N种方法,实在是打包不出来,若有大佬知道,还望告诉老妹!!!😎

所以为什么要优化

公司在发展,客户在提要求,当遇到一个客户像批改论文给我的网页提需求的时候😟😟😟,我想每个小前端都很崩溃吧!对就是这样我走上了对这个项目的漫漫优化之路🥹🥹🥹

优化之路

面临的问题:

此项目最大的问题之一就是文件加载过多,其二就是涉及到一张高清图片的计算,必须等高清图片加载完成计算之后再打开,其三是该项目一个文件承载了很多种页面的可能性,之前没有对三方库进行加载及加载顺序的管理,导致所有三方库无论有没有用都被加载了。所以导致页面打开极慢!问题找到了就好解决了!🌈🌈🌈

计划:

1、改造目前所有的计算js文件,改为函数单一返回模式。

2、改造所有html模块文件,使用nunjucks模版提供的函数来控制展示。

3、将高清图片的加载放在异步加载中,同时改变此模块对应的html处理逻辑。

4、建立公共加载模块,分类对三方库进行加载管理,控制是否加载及加载顺序。

5、建立脚本.sh文件,使用cat和uglifyjs,对所有计算js文件及html文件进行打包压缩。

实施:

第一步:

所有计算js文件统一修改为

kotlin 复制代码
 export const data = (core) => {
    if (!core) return
    result = core
    return result
};

所有nunjucks模版提供的函数来控制展示

csharp 复制代码
{% macro Info() %}
<div class="Info">
</div>
{% endmacro %} 

将高清图片的加载放到异步

ini 复制代码
export const onloadImage = (url) => {
  const getImg = (url) =>
    new Promise((resolve, reject) => {
      const img = new Image();
      img.src = url;
      img.onload = function () {
        resolve({ height: img.height, width: img.width });
      };
    });
let imginfo = await getImg(url);
}

setTimeout(async function () {
      let imageinfo = await onloadImage();
},500)

建立公共文件onload_ExtPack.js控制文件加载

ini 复制代码
//加载js
export function loadScript(url, type = "text/javascript") {
  return new Promise(function (resolve, reject) {
    var script = document.createElement("script");
    script.type = type;
    script.src = url;
    document.body.appendChild(script);
    script.onload = function () {
      resolve(true);
    };
  });
}
//加载css
export function loadStyle(url) {
  let link = document.createElement("link");
  link.rel = "stylesheet";
  link.type = "text/css";
  link.href = url;
  document.getElementsByTagName("head")[0].appendChild(link);
}

export async function ExtPack(extpack) {
    if (extpack.xxx){
    await loadScript(
      "./js/xxx?v=" + version,
      "text/javascript"
    );
     loadStyle(
      "./css/xxx.min.css"
    )
    }
}

建立脚本文件,我的做法是由一个主sh脚本文件来读取配置sh脚本文件的内容进行打包

主脚本.sh

bash 复制代码
# 配置sh脚本文件的路径
Pack_Path=./pack

# 配置sh脚本文件
pack_project=(

    配置1:pack1
    
    配置2:pack2
    
    配置3:pack3
    
)


echo "开始合并文件..."

echo  "文件类型:\n js or html "

read -p "请输入要合并的文件类型 => " type     

if  [ "$type" == "html" ]; then

echo  "已配置脚本文件:\n ${pack_project[*]} "

read -p "配置sh脚本文件  => "  code

source $Pack_Path/$code.sh

echo "合并文件目录: $html_PATH "

cat ${html_PATH[*]} > $html_PATH_EXIT

echo -e "合并 $html_PATH_EXIT 完成 "

cat  $html_PATH_EXIT $html_PATH_EXIT_cat2 > $cat_PATH

echo -e " 合并 $cat_PATH 完成 "

echo "合并压缩结束..."

fi
  
if  [ "$type" == "js" ]; then

echo  "已配置脚本文件:\n ${pack_project[*]} "

read -p "配置sh脚本文件  => "  code

source $Pack_Path/$code.sh


cat ${js_PATH_LIST[*]} > $js_PATH_EXIT

echo -e " 合并  $js_PATH_EXIT 完成 "

uglifyjs $js_PATH_EXIT -m -o $js_PATH_EXIT_MIN

echo -e " 压缩 $js_PATH_EXIT_MIN 完成 "

echo "合并压缩结束..."

fi

配置脚本 pack1.sh 目录为pack/pack1.sh

ini 复制代码
### 需要打包的html ###
html_PATH=(
    ./html/*.html
    )

### 打包后的html存储地址###
html_PATH_EXIT=./program/html/pack1_cat1.html  

### 模版1 ###
html_PATH_EXIT_cat2=./program/html/pack1_cat2.html 

### html存储地址 ###
cat_PATH=./report/html/pack1.html

# 需要合并的js文件
js_PATH_LIST=(
    ./js/*.js
)
# js存储地址
js_PATH_EXIT=./build/js/pack1.js
js_PATH_EXIT_MIN=./build/js/pack1.min.js
第二步

基本的步骤已经结束了,运行主文件./pack.sh,就能实现js和html的合并和压缩了

将文件加载替换成打包好的文件

中间总结

目前是基本已经实现了对项目的优化,基本的速度提升是原项目十倍往上!!!👏👏👏客户需求已经达标了。但这就完了吗!不可能,绝对不可能😎

客户的要求达到了,但这个方式最大的缺陷是每次改动都要进行手动运行.sh文件,再输入进行打包之后调试,往往一个小改动或者测试某个函数的时候就会非常麻烦!极其麻烦,而这个项目改动非常频繁。客户舒服了,领导对结果满意了,但对于维护者来说很折磨人。最气人的是😤😤😤,还就折磨我个小小前端😭😭😭。

改!必须改!

怎么改

webpack的打包模式怎么样!!!很不错吧,实时监控文件的改动进行打包,既然webpack能做到我也能做到一点点🤏

查了一下webpack使用nodejs来写的,俺不会咋个办,那就用golang写个服务替代吧!

golang时时监控,调用.sh文件来实现打包服务!

go 复制代码
package main

import (
	"bufio"
	"fmt"
	"github.com/fsnotify/fsnotify"
	"io"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
)

func main() {
	watch := NewNotifyFile()
	watch.WatchDir("./src")
	select {}

}

type NotifyFile struct {
	watch *fsnotify.Watcher
}

var REPORT string

func NewNotifyFile() *NotifyFile {
	fmt.Scan(&REPORT)
	fmt.Println("配置sh文件名:", REPORT)
	w := new(NotifyFile)
	w.watch, _ = fsnotify.NewWatcher()
	return w
}

// 监控目录
func (this *NotifyFile) WatchDir(dir string) {

	//通过Walk来遍历目录下的所有子目录
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		//判断是否为目录,监控目录,目录下文件也在监控范围内,不需要加
		if info.IsDir() {
			path, err := filepath.Abs(path)
			if err != nil {
				return err
			}
			err = this.watch.Add(path)
			if err != nil {
				return err
			}
		}
		return nil
	})

	go this.WatchEvent() //协程
}

func (this *NotifyFile) WatchEvent() {
	for {
		select {
		case ev := <-this.watch.Events:
			{
				if ev.Op&fsnotify.Create == fsnotify.Create {
					file, err := os.Stat(ev.Name)
					if err == nil && file.IsDir() {
						this.watch.Add(ev.Name)
						fmt.Println("添加监控 : ", ev.Name)
					}
				}

				if ev.Op&fsnotify.Write == fsnotify.Write {
					filesuffix := path.Ext(ev.Name)
						fmt.Println("文件后缀名:", filesuffix, filesuffix == ".js")
						fmt.Println("修改文件 : ", ev.Name)
						if filesuffix == ".js" || filesuffix == ".html" {
							write, err := os.OpenFile("pack.sh", os.O_RDWR, 0666)
							if err != nil {
								fmt.Print(err.Error())
							}
							defer write.Close()

							reader := bufio.NewReader(write)
							pos := int64(0)
							var pack_type string
							if filesuffix == ".js" {
								pack_type = "js"
							}
							if filesuffix == ".html" {
								pack_type = "html"
							}
							for {
								//读取每一行内容
								line, err := reader.ReadString('\n')
								if err != nil {
									if err == io.EOF {
										fmt.Println("脚本已更新完毕!")
										_, err := exec.Command("/bin/bash", "./pack.sh").CombinedOutput()
										if err != nil {
											fmt.Print(err.Error())
											break

										} else {
											fmt.Print("脚本执行成功!:")

										}

										break
									} else {
										fmt.Println("发生了错误:", err)
										return
									}
								}
								if strings.Contains(line, "PACK_TYPE=") {
									fmt.Println("开始更新打包脚本...")
									bytes := []byte("PACK_TYPE=" + pack_type + "\n\n\n\n\n" + "PACK_CODE=" + REPORT + "\n\n\n\n\n\n\n\n\n\n\n\n")
									write.WriteAt(bytes, pos)

								}
							}

						
					}

				}

				if ev.Op&fsnotify.Remove == fsnotify.Remove {
					fmt.Println("删除文件 : ", ev.Name)
					fi, err := os.Stat(ev.Name)
					if err == nil && fi.IsDir() {
						this.watch.Remove(ev.Name)
					}
				}
			}
		case err := <-this.watch.Errors:
			{
				fmt.Println("error : ", err)
				return
			}
		}
	}
}

监控加好了,把主.sh文件也改一下🌝

bash 复制代码
PACK_TYPE=html

PACK_CODE=HM_MA_2201_V


Pack_Path=./report/pack

pack_project=(
    配置1:pack1
    
    配置2:pack2
    
    配置3:pack3
)

if  [ "$PACK_TYPE" == "html" ]; then

source $Pack_Path/$PACK_CODE.sh

cat ${html_PATH[*]} > $html_PATH_EXIT

cat  $html_PATH_EXIT $html_PATH_EXIT_cat2 > $cat_PATH

fi
  
if  [ "$PACK_TYPE" == "js" ]; then

source $Pack_Path/$PACK_CODE.sh


cat ${js_PATH_LIST[*]} > $js_PATH_EXIT

uglifyjs $js_PATH_EXIT -m -o $js_PATH_EXIT_MIN

fi

就这样!实现了

结束🌹🌹🌹

好歹是实现了运行该服务的时候能实现自动打包,不用重复去手动执行.sh文件!但我用golang实现监控和替换文件变量的方式比较粗暴🌚🌚🌚,但在我自己的可控范围之内,还是极大的节省了我的调试时间!

不知道各位大佬有没有更牛逼的实现方式,或者还有什么可以改进的地方!欢迎各位大佬指教。老妹在此感谢大家的阅读,我们一起共同进步!🫶🫶🫶🌹🌹🌹

相关推荐
前端Hardy几秒前
HTML&CSS:产品卡片动画效果
前端·javascript
货拉拉技术6 分钟前
货拉拉开源:鸿蒙路由 TheRouter
android·前端·harmonyos
中杯可乐多加冰8 分钟前
工业4.0数字孪生新引擎:星图云开发者平台全景评测
前端·低代码·掘金·金石计划
云边小卖铺.15 分钟前
运行vue项目报错 errors and 0 warnings potentially fixable with the `--fix` option.
前端·javascript·vue.js
我是若尘16 分钟前
前端处理大量并发请求的实用技巧
前端
Lstmxx18 分钟前
Electron:使用数据流的形式加载本地视频
前端·electron·node.js
JunjunZ25 分钟前
unibest框架开发uniapp项目:兼容小程序问题
前端·vue.js
lyc23333327 分钟前
鸿蒙Next应用启动框架AppStartup:流程管理与性能优化🚀
前端
Data_Adventure27 分钟前
Vue 3 作用域插槽:原理剖析与高级应用
前端·vue.js
curdcv_po35 分钟前
报错 /bin/sh: .../scrcpy-server: cannot execute binary file
前端