小前端的项目优化之如何干到用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实现监控和替换文件变量的方式比较粗暴🌚🌚🌚,但在我自己的可控范围之内,还是极大的节省了我的调试时间!

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

相关推荐
崔庆才丨静觅9 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606110 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了10 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅10 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅11 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment11 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅11 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊11 小时前
jwt介绍
前端
爱敲代码的小鱼11 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax