整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

使用最快的 Rust web 框架 Farm 迁移最

使用最快的 Rust web 框架 Farm 迁移最火的 Vue3 后台管理系统

arm 介绍

可能很多同学还不知道 Farm , 先给大家介绍一下, Farm 是一个基于 Rust 的 Web 构建工具, 类似 webpack 和 vite,但更快。 Farm 用 resolve, load, transform 把所有的 asset (js/jsx/ts/tsx、css/sass/less、html、静态资源、json 等),并将它们打包成一系列可部署文件。 Farm 作为一个速度极快的构建工具,可帮助您更快的构建出 web / nodejs 应用程序。

Farm 特性以及设计理念

  • ? 超级快: 使用 Rust 编写, 可以在毫秒级别内启动一个 React 或 Vue 项目。 在大多数情况下, 可以在 10ms 内执行 HMR 的更新, 理论上任何项目热启动在开启增量编译的情况下启动时间不会超过 1 秒。
  • ? 增量构建: 支持持久缓存,以模块粒度进行缓存复用,任何一个模块如果没有改变,始终只会编译一次!
  • 完全可拔插: Farm 由插件驱动, 兼容 Vite, Rollup, Unplugin 插件,同时支持 Farm 编译插件(Rust 和 JavaScript 插件,以及 Swc 插件),Farm Runtime 插件,Farm Dev Server 插件。
  • ?? 丰富的编译能力支持: 开箱即用, Farm 内置了 JS/TS/JSX/TSX、CSS、Css Modules、Sass、Less、Postcss,HTML 和静态资源的编译,支持 React,Vue,Solid, Svelte, Preact 等框架。
  • ?? 懒编译: 仅仅在请求时才编译动态导入的资源,极大提速大型项目的编译。通过 dynamic import 即可启用,被 dynamic import 的模块及其依赖仅在使用时才会编译。
  • 局部打包: 自动根据依赖关系、资源大小,将项目打包成若干个资源,提升资源加载性能的同时,保证缓存命中率。参考 RFC-003 Partial Bundling
  • 一致性: 开发环境和生产环境的表现一致,所见即所得。
  • 兼容性: 同时支持传统(ES5)和现代浏览器。

从 vite 项目中如何迁移

接下来就进入正题, 那么如何从已有的 vite 项目迁移到 Farm 呢 ?

从 vite 迁移到 farm 其实非常简单, 因为 farm 内置了对 vite 插件的兼容, 所以您只需要做的是将 vite.config.ts 转换成 farm.config.js。

  • 参考 Configuring Farm 将 Farm 的配置选项映射到 vite 配置
  • 对于 Vite Plugins,将 vite.config.ts 中的plugins移动到 farm.config.ts 中的 vitePlugins

但是需要注意的是

  • 对于某些 vite 插件例如: unocss 这种与 vite 深度集成的插件, 由于内部设计的差异问题, 会导致某些插件与 Farm 并不兼容,
  • 一些 Vite 配置选项在 Farm 中是不需要的,例如 optimizeDeps,您可以在迁移到 Farm 时忽略这些选项
  • 对于 SSR,您需要将其重构为Farm SSR

迁移基础项目

我们先从基础项目来体会一下从 vite 迁移到 Farm 有多简单

首先还是我们熟悉的创建 vite 项目

pnpm create vite

我们选一个 Vue 项目模版, 创建好项目之后, 我们的 vite.config.ts 应该是这样的

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
})

那么把大象放进冰箱一共需要三步

  1. 第一步: 安装依赖
pnpm install @farmfe/core @farmfe/cli -D
  1. 第二步: 新增 farm.config.ts 文件,Farm 在插件系统 和 hmr 做了一套适配器来兼容 vite 生态, 所以这里我们就可以直接使用 vite 插件啦
import { defineConfig } from '@farmfe/core'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  vitePlugins: [vue()],
})
  1. 第三步: 啊 没有第三步了, 因为我们已经迁移完成啦。

然后我们在控制台执行 npx farm start 控制台就可以看到以下命令

执行构建命令 npx farm build

这样就大功告成啦, 是不是非常简单。

迁移 vue-pure-admin

接下来我们来迁移一个比较复杂的项目, vue-pure-admin 是一个全面ESM+Vue3+Vite+Element-Plus+TypeScript 编写的一款后台管理系统, 并且模块数量算是比较大, 那么我们使用 Farm 来迁移这个项目, 来进一步体现出 Farm 的优势. 以及迁移的成本到底有多低

首先我们先克隆一下 pure-admin 的仓库代码, 然后查看一下项目的 vite 配置文件, 并提示出迁移所需要修改的地方

import { getPluginsList } from "./build/plugins";
import { include, exclude } from "./build/optimize";
import { type UserConfigExport, type ConfigEnv, loadEnv } from "vite";
import {
  root,
  alias,
  warpperEnv,
  pathResolve,
  __APP_INFO__
} from "./build/utils";

export default ({ mode }: ConfigEnv): UserConfigExport=> {
  const { VITE_CDN, VITE_PORT, VITE_COMPRESSION, VITE_PUBLIC_PATH }=warpperEnv(loadEnv(mode, root));
  return {
    base: VITE_PUBLIC_PATH,
    root,
    resolve: {
      alias
    },
    // 服务端渲染
    server: {
      // 端口号
      port: VITE_PORT,
      host: "0.0.0.0",
      // 预热文件以提前转换和缓存结果,降低启动期间的初始页面加载时长并防止转换瀑布
      warmup: {
        clientFiles: ["./index.html", "./src/{views,components}/*"]
      }
    },
    plugins: getPluginsList(VITE_CDN, VITE_COMPRESSION),
    // https://cn.vitejs.dev/config/dep-optimization-options.html#dep-optimization-options
    optimizeDeps: {
      include,
      exclude
    },
    build: {
      // https://cn.vitejs.dev/guide/build.html#browser-compatibility
      target: "es2015",
      sourcemap: false,
      // 消除打包大小超过500kb警告
      chunkSizeWarningLimit: 4000,
      rollupOptions: {
        input: {
          index: pathResolve("./index.html", import.meta.url)
        },
        // 静态资源分类打包
        output: {
          chunkFileNames: "static/js/[name]-[hash].js",
          entryFileNames: "static/js/[name]-[hash].js",
          assetFileNames: "static/[ext]/[name]-[hash].[ext]"
        }
      }
    },
    define: {
      __INTLIFY_PROD_DEVTOOLS__: false,
      __APP_INFO__: JSON.stringify(__APP_INFO__)
    }
  };
};

farm 配置基本平替 vite 配置, 我们再来看一下迁移到 farm 所需要修改的配置文件,由于对 css 的处理 farm 和 vite 的处理方向上的差异,所以我们只需要把一些 css 的插件配置替换成 farm 的插件, 其他的 vite 插件完全可以复用

import { getPluginsList } from "./build/plugins";
import { type UserConfigExport, type ConfigEnv, loadEnv } from "@farmfe/core";
import { root, alias, warpperEnv, __APP_INFO__ } from "./build/utils";
import postcss from "@farmfe/js-plugin-postcss";
import sass from "@farmfe/js-plugin-sass";

export default ({ mode }: ConfigEnv): UserConfigExport=> {
  const { VITE_CDN, VITE_PORT, VITE_COMPRESSION, VITE_PUBLIC_PATH }=warpperEnv(loadEnv(mode, root)[0]);
  return {
    compilation: {
      output: {
        publicPath: VITE_PUBLIC_PATH,
        targetEnv: "browser-es2015",
        filename: "static/[ext]/[name]-[hash].[ext]",
        assetsFilename: "static/[ext]/[name]-[hash].[ext]"
      },
      resolve: {
        alias
      },
      script: {
        plugins: [
          {
            name: "@swc/plugin-remove-console",
            options: {
              exclude: ["error"]
            },
            filters: {
              moduleTypes: ["js", "ts", "jsx", "tsx"]
            }
          }
        ]
      },
      externalNodeBuiltins: false,
      define: {
        __INTLIFY_PROD_DEVTOOLS__: false,
        __APP_INFO__: process.env.FARM_FE
          ? __APP_INFO__
          : JSON.stringify(__APP_INFO__)
      }
    },
    root,
    // 服务端渲染
    server: {
      // open: true,
      port: VITE_PORT
    },
    plugins: [
      sass({
        legacy: true
      }),
      postcss(),
      {
        name: "remove-css-filter-plugin",
        priority: 0,
        transform: {
          filters: {
            resolvedPaths: ["element-plus/dist/index.css"]
          },
          async executor({ content }) {
            return {
              content: content.replace(/filter:\s*alpha\(opacity=0\);/g, "")
            };
          }
        }
      }
    ],
    vitePlugins: getPluginsList(VITE_CDN, VITE_COMPRESSION)
  };
};

我们可以看到 vite 插件完全复用, 需要修改的地方仅仅是一些编译输出的属性, 整体最大的修改点只有这些,我在 vue-pure-admin 项目提了一个 pr, 还有一些设计差异的小细节大家可以在这里 查看 尽管从 Vite 迁移到 Farm 需要进行一些调整和适配,但是这个过程并不复杂,而且可以通过优化提升项目的性能和开发效率。因为确实迁移很简单, 所以本篇文章只是简单的介绍一下 Farm 目前的可用度,随着 Farm 的不断发展和完善,相信它会成为更多项目的首选构建工具。

那么到了大家最关心的一点, 那么迁移之后的性能到底怎么样呢?

在开启增量构建的情况下的项目启动对比

注:因为 vite 对于源代码是请求时编译,所以此处加上了 “项目可访问时间”(即 “热启动时间” + “页面加载时间”),作为另一个指标来综合对比性能。所以 Farm 在热启动比 vite 快 5 倍左右

结语

farm 已经具有了 所有开发调试 发布 也有很大的提升空间

Farm 开源已经一年多了, 也已经正式的发布了 1.0 版本, 已经达到了生产可用的状态, 不仅仅是从 vite 中迁移, 在 webpack 中迁移, 团队中也做了非常多的尝试, 迁移成本都很低, Farm 下一步计划基于 Farm 打造出下一代 SSR 框架, 也希望可以有更多的同学参与进来, 共同学习进步。

未来,我们将继续撰写更多的文章,深入介绍 Farm 的各项功能以及特性和原理解析,帮助大家更好地理解和使用下一代的构建工具,可以对前端工程化以及代码解析与编译的原理理解之后, 可以更加深入地了解现代前端开发的核心概念和技术,并掌握构建高效、稳定的前端项目的关键技能。让我们共同期待未来的探索和发现,共同探讨前端技术的前沿话题,为构建更加美好的 Web 世界贡献一份力量。


链接:https://juejin.cn/post/7352837711339814963

迎搜索公众号:白帽子左一

每天分享更多黑客技能,工具及体系化视频教程(免费领)

来源:HACK学习呀

一直找不到目标站点,昨天下午收到的一条微信之后突然有了目标



还是老规则 下载了APP 这里提示下注意事项

因为这种APP是自动采用微信账号登录 且苹果手机登录前需要申请数据网络权限

所以在进行抓包前 ,需要先点开APP给予数据网络权限并提前登录微信账号(设置代理之后无法登录微信


进入APP后、 首先对APP内部通过http请求获取或得到数据的接口进行了测试, 也测试得到APP走http请求的IP为 ,阿里云服务器IP地址

注意事项 碰到阿里云服务器(不要进行端口扫描,不要进行网页路径探测)

因为这两点都会让阿里云封你的IP 首先对反馈接口进行了抓包 丢入了XSS


丢入xss 之后 考虑到这个APP并没有什么可以入手的点

(本人比较菜,没办法在这上面找到突破口)

于是注意到这个APP有挂载的官网,果断从官网开始入手


首先找到了部分代理登录的后台,进入了登录界面,因为有两个登录界面 一个是http 并且无验证码

一个是https 有验证码 首先从http无验证码口开始爆破密码

https://jingyan.baidu.com/article/200957619c8739cb0721b4ff.html
Burp爆破网站后台账号密码步骤


成功的登录了后台,发现后台并没有什么其他功能 只是能查看个人的代理及充值返利情况

在未找到直接能getshell的点, 首先对网站后台进行了抓包, 查看后台中部分搜索功能是否存在SQL注入,其次查看后台是否存在逻辑漏



可以确定的点如下(网站后台未存在有SQL注入,数据库不进行报错,对网站进行扫描并未封IP
IP地址为武汉,极有可能是源IP地址,接着继续寻找网站的逻辑漏洞


当咱们对myuser.php 进行访问时 服务器缓存的cookie 信息为以上图片内
aliyungf_tc 为无效数据 可无视不计算
user_name 为账号信息 user_id为服务器uid值 user_level为用户等级
尝试修改账号信息为123456789 数据并未发生改变
尝试修改uid值 数据发生改变
尝试修改用户等级 未发生改变
于是得到uid 值为判断用户的标准


接下来 就找我亲爱的丁哥写了套爬虫 (用户uid值为循环上升,爬取页面内容 得到网站总用户量为1万五千人)

之前咱们提到还有一个https的后台
有验证码机制


有验证码机制进行爆破成功率可能并不是很高 尝试进行找回密码功能



修改uid值为空试试


点击确认提交 直接来到了修改密码的页面
https://qy.xxxxxxx.com/user_goback_password.php?uid=
再进行uid补全为test用户uid=11000 输入更改的新密码 更改成功



成功进入后台 截止到目前为止

(拿到了网站全部用户的个人信息,拿到了充值游戏币的权限)

顺便解释下为什么能更改成功test用户的密码


当user_goback_quan.php?uid=11000 修改为uid=空时


数据库查询不到用户 那么对应的答案也是查询不到 为空的


更改密码在数据库判断中 问题=答案(正确) ----- 更改成功


当问题不存在 答案不存在的情况下 问题=答案 跳转到更改密码界面 — 更改uid值为test用户uid=====成功更改test用户密码

于Element UI的后台管理基础界面搭建

  • 安装element UI
// 在已经创建好的项目中安装插件
vue add element
// 安装过程中的选项
?  Successfully installed plugin: vue-cli-plugin-element

? How do you want to import Element? Fully import
? Do you wish to overwrite Element's SCSS variables? No
? Choose the locale you want to load zh-CN
  • 运行验证
npm run serve
// 运行结果
DONE  Compiled successfully in 6537ms                         22:40:55

  App running at:
  - Local:   http://localhost:8080/
  - Network: http://192.168.1.9:8080/


  • 安装路由插件
vue add router

?  Installing @vue/cli-plugin-router...

+ @vue/cli-plugin-router@4.4.4
updated 1 package in 16.079s

42 packages are looking for funding
  run `npm fund` for details

?  Successfully installed plugin: @vue/cli-plugin-router

? Use history mode for router? (Requires proper server setup for index
fallback in production) No `N`
  • 目录结构


新建主界面组件Main.vue

// 1.根据element Container 布局容器创建主界面
<template>
  
</template>

<script>
export default {

}
</script>

<style>

</style>
// 2.修改router下index.js
import Main from '../views/Main.vue'
{
    path: '/',
    name: 'main',
    component: Main
}
// 3.简化App.vue 只保留router-view
<div id="app">
    <router-view/>
</div>
// 4.去除App.vue 中样式,初始化样式表 margin: 0;padding: 0;
// 5.修改Main.vue 中高度样式表style="height: 100vh;"

Main.vue引入Container 布局容器

//保留基本结构
<template>
  <el-container style="height: 100vh;">
  <el-aside width="200px" style="background-color: rgb(238, 241, 246)">
    <el-menu :default-openeds="['1', '3']">
      <el-submenu index="1">
        <template slot="title"><i class="el-icon-message"></i>导航一</template>
          <el-menu-item index="1-1">选项1</el-menu-item>
          <el-menu-item index="1-2">选项2</el-menu-item>
          <el-menu-item index="1-3">选项3</el-menu-item>
      </el-submenu>
    </el-menu>
  </el-aside>
  
  <el-container>
    <el-header style="text-align: right; font-size: 12px">
      <el-dropdown>
        <i class="el-icon-setting" style="margin-right: 15px"></i>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item>查看</el-dropdown-item>
          <el-dropdown-item>新增</el-dropdown-item>
          <el-dropdown-item>删除</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
      <span>王小虎</span>
    </el-header>
    
    <el-main>
      <el-table :data="tableData">
        <el-table-column prop="date" label="日期" width="140">
        </el-table-column>
        <el-table-column prop="name" label="姓名" width="120">
        </el-table-column>
        <el-table-column prop="address" label="地址">
        </el-table-column>
      </el-table>
    </el-main>
  </el-container>
</el-container>
</template>

<style>
  .el-header {
    background-color: #B3C0D1;
    color: #333;
    line-height: 60px;
  }
  
  .el-aside {
    color: #333;
  }
</style>

<script>
  export default {
    data() {
      const item={
        date: '2020-08-08',
        name: '墨客moke',
        address: '青海省西宁市'
      };
      return {
        tableData: Array(20).fill(item)
      }
    }
  };
</script>

创建分类列表

// 管理后台客户端分类列表创建
// Main.vue
// 1:新建分类、分类列表
// 2:添加 router 属性
// 3:导航菜单添加路由
<el-menu router :default-openeds="['1']"> <!-- 导航菜单收缩 --> <!-- router属性 -->
      <el-submenu index="1">
        <template slot="title"><i class="el-icon-message"></i>内容管理</template>
        <el-menu-item-group>
          <template slot="title">分类</template>
          <el-menu-item index="/categories/create">新建分类</el-menu-item>
          <el-menu-item index="/categories/list">分类列表</el-menu-item>
        </el-menu-item-group>
      </el-submenu>
   </el-menu>

新建CategoriesEdit组建

为Main.vue

新建CategoriesEdit子组件

// 1.CategoriesEdit.vue

// 2.添加Main子路由
// 3.请求远程数据接口

安装axios插件

// 安装插件
F:\client\node-vue-moba\admin>cnpm i axios
√ Installed 1 packages
√ Linked 3 latest versions
√ Run 0 scripts
√ All packages installed (4 packages installed from npm registry, used
 593ms(network 587ms), speed 26.3kB/s, json 4(15.44kB), tarball 0B)

// 配置使用
// http.js
import axios from 'axios'

const http=axios.create({
  baseURL : 'http://localhost:3000/admin/api'
})

export default http


// main.js 创建axios实例
import http from './http'
Vue.prototype.$http=http

服务端接口开发

查看相关内容见 0x04CRUD接口开发

完善列表内容CategoriesEdit.vue

<template>
  <div class="about">
    <h1>{{id ? '编辑' : '新建'}}分类</h1>
    <el-form label-width="120px" @submit.native.prevent="save">
      <el-form-item label="名称">
        <el-input v-model="model.name"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" native-type="submit">保存</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  props:{
    id:{}
  },
  data(){
    return {
      model:{}
    }
  },
  methods:{
    async save(){      
      // 列表新建和编辑数据更新
      if (this.id) {
        await this.$http.put(`categories/${this.id}`,this.model)
      } else {
        await this.$http.post('categories',this.model)
      }
      //  跳转至分类列表
      this.$router.push('/categories/list')
      this.$message({
        type: 'sucess',
        message: '保存成功'
      })
    },
    // 分类编辑1:获取分类数据
    // 分类编辑2:修改后更新数据
    async fetch(){
      const res=await this.$http.get(`categories/${this.id}`)
      this.model=res.data
    }
  },
  created(){
    this.id && this.fetch()
  }
}
</script>

<style>

</style>

新建分类列表

<template>
  <div class="about">
    <h1>分类列表</h1>
    <el-table :data="items">
      <el-table-column prop="_id" label="ID" width="245"></el-table-column>
      <el-table-column prop="name" label="分类名称"></el-table-column>
      <el-table-column fixed="right" label="操作" width="180">
        <template slot-scope="scope">
          <el-button type="text" size="small" @click="$router.push(`/categories/edit/${scope.row._id}`)">编辑</el-button>
          <el-button type="text" size="small" @click="remove(scope.row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
export default {
  data(){
    return {
      items:[]
    }
  },
  methods:{
    async fetch(){
     const res=await this.$http.get('categories')
     this.items=res.data
    },
    async remove(row){
       this.$confirm(`是否确定删除${row.name}?`, '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(async ()=> {
          await this.$http.delete(`categories/${row._id}`),
          this.$message({
            type: 'success',
            message: '删除成功!'
          });
          this.fetch()
        })          
      }
  },
  created(){
    this.fetch()
  }
}
</script>

<style>

</style>

感谢阅读,欢迎交流。