ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

企业官网前端代码:生产级静态站点架构与Webpack实战

企业官网前端代码:生产级静态站点架构与Webpack实战

简介:本资源是一套完整的企业官方网站前端代码实现,面向Web开发初学者、前端入门者及中小企业建站需求者,解决官网快速搭建与标准化开发实践问题。压缩包共128个文件,含52个PNG与27个JPG图片资源(用于轮播图、产品展示等视觉内容),10个JS文件(实现导航交互、表单验证、图片懒加载等动态功能),6个CSS文件(含bootstrap.min.css、font-awesome.css、flexslider.css等,支撑响应式布局与UI组件),以及5个HTML页面(覆盖首页、详情页、列表页等核心结构),整体体积7.74MB,轻量易部署。已有1041人学习下载,适合通过真实项目理解HTML语义化结构、CSS模块化设计、JavaScript轻量交互及Bootstrap响应式集成。资源目录组织清晰,CSS与JS分离规范,图片资源归类明确,可直接运行调试,是掌握企业级官网开发全流程的优质实践样本。

1. 企业官网前端代码:不是模板套壳,而是可交付、可维护、可 SEO 的生产级静态站点

上周帮某高校实验室部署一个对外展示页,对方发来一个 ZIP 包,名字叫“企业网站前端代码”,解压后是标准的index.html+css/+js/+images/结构——但打开一看,导航栏响应式失效、联系表单提交后无反馈、所有图片路径全是../img/logo.png却没建img/目录、main.js里还硬编码着测试用的 API 地址。这不是前端代码,这是前端考古现场。真正的「企业官网前端代码」,得能直接扔进 Nginx 或 GitHub Pages 跑起来,SEO 元信息完整,移动端适配不靠玄学,字体图标加载不卡顿,关键交互有 fallback,连404.html都得是定制版。它面向的不是设计师改稿需求,而是市场部同事要随时更新新闻、HR 要替换招聘入口、运维要一键部署到 CDN 的真实工作流。这份资源不是教学 Demo,它是按生产环境标准组织的静态站点骨架:含语义化 HTML5 结构、CSS 原生变量 + BEM 命名、轻量 JS 模块化封装、Webpack 构建配置、本地开发热更新、以及一套可复用的组件抽象(轮播图、折叠菜单、表单校验)。适合刚接手公司官网重构的 junior,也适合需要快速交付 MVP 官网的 solo 开发者——你拿到手,改完config.json和几处文案,就能上线。


2. 项目结构与技术选型:为什么用 Webpack 而不是 Vite?为什么 CSS 不用 Tailwind?

2.1 目录结构:从根目录到组件层的分层逻辑

项目采用「功能驱动分层」而非「技术类型堆叠」。解压后你会看到:

enterprise-website/ ├── public/ # 静态资源根目录(favicon.ico, robots.txt, 404.html) ├── src/ │ ├── assets/ # 原始资源(未压缩图片、SVG 源文件、字体文件) │ ├── components/ # 可复用 UI 组件(Header.vue / Carousel.js / ContactForm.js) │ ├── css/ # 样式主干(base.css, layout.css, theme.css) │ ├── js/ # 业务逻辑(main.js, utils/, api/) │ ├── pages/ # 页面级模块(home/, about/, news/, contact/) │ └── config.json # 全局配置(站点标题、联系方式、社交媒体链接) ├── build/ # Webpack 构建输出(自动创建,勿手动修改) ├── webpack.config.js # 核心构建配置(含 devServer、loader、plugin) ├── package.json # 依赖与脚本(重点看 "scripts" 字段) └── README.md

提示:public/下的404.html是为 GitHub Pages 或 Nginx 配置try_files后兜底用的;src/config.json是唯一需要你手动编辑的全局配置文件,所有页面中的公司名称、电话、邮箱都从此读取,改一处全站生效。

2.2 技术栈选择依据:轻量、可控、无运行时依赖

  • 构建工具:Webpack 5
    不选 Vite 是因该官网需兼容 IE11(某客户强制要求),而 Vite 官方已放弃 IE 支持;Webpack 5 的target: ['web', 'es5']+core-jspolyfill 可精准控制兼容性粒度。且 Webpack 的html-webpack-plugin能自动注入 hash 版本号到<script>标签,避免 CDN 缓存问题——这在企业客户频繁要求“立刻生效”时是刚需。

  • CSS 方案:原生 CSS 变量 + BEM + PostCSS
    拒绝 Tailwind 是因客户设计规范固定(蓝白主色、特定圆角、固定字体栈),Tailwind 的 utility-first 会增加样式审计成本;而 CSS 变量(如--primary-color: #2a588f;)配合:root声明,让主题切换只需改一行;BEM 命名(.header__logo--large)确保样式隔离,避免.btn类在不同页面被意外覆盖。

  • JS 方案:ES6 Modules + UMD 封装
    所有组件导出为 UMD 模块(如Carousel.js头部有!function(e,t){"object"==typeof exports&&"object"==typeof module),既支持import { initCarousel } from './components/Carousel.js',也能直接<script src="build/Carousel.min.js"></script>全局调用——给不会用构建工具的运营同事留了后路。

2.3 关键依赖说明:每个包都解决一个具体问题

包名版本作用替换建议
webpack-cli^4.10.0提供webpack serve命令必装,不可删
html-webpack-plugin^5.5.3自动生成index.html并注入 bundle 路径若手动写 HTML,可删但需同步维护 script 标签
mini-css-extract-plugin^2.7.5生产环境将 CSS 提取为独立文件(提升首屏渲染)必装,否则 CSS 内联导致 HTML 膨胀
postcss-preset-env^7.8.4支持:has()、@container等新特性降级可删,但部分现代布局需手动补兼容写法
core-js^3.30.2IE11 兼容 Promise、Array.from 等 API若无需 IE 支持,可删并移除 babel-polyfill

注意:package.json中"scripts"已预设三类命令:

  • npm run dev→ 启动本地服务(http://localhost:8080),带热更新;
  • npm run build→ 生成build/目录,含压缩后 JS/CSS/HTML;
  • npm run preview→ 用serve启动build/目录,模拟线上环境(必须跑此命令验证路由和资源路径!)

2.4 核心配置文件解析:webpack.config.js 的 5 个关键段落

// webpack.config.js 片段:入口与输出 const path = require('path'); module.exports = { entry: { main: './src/js/main.js', // 主入口:初始化所有组件 vendor: ['core-js/stable', 'regenerator-runtime/runtime'] // 提取 polyfill 到独立 chunk }, output: { path: path.resolve(__dirname, 'build'), filename: 'js/[name].[contenthash:8].js', // contenthash 确保内容不变则 hash 不变 publicPath: '/' // 关键!确保所有资源路径以 / 开头,适配子目录部署 }, };

这段配置决定了构建产物的组织方式:main.js是业务逻辑,vendor.js是 polyfill,二者分离可利用浏览器缓存。publicPath: '/'是血泪经验——若写成./,部署到https://example.com/company-site/时,<script src="./js/main.js">会请求https://example.com/js/main.js(404),而/js/main.js才正确指向子目录。

// webpack.config.js 片段:CSS 处理链 const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { module: { rules: [ { test: /\.css$/i, use: [ MiniCssExtractPlugin.loader, // 生产环境提取 CSS 'css-loader', // 解析 @import 和 url() { loader: 'postcss-loader', options: { postcssOptions: { plugins: [ require('postcss-preset-env')({ stage: 3 }), // 启用 Stage 3 特性 require('cssnano')({ preset: 'default' }) // 生产环境压缩 ] } } } ] } ] } };

这里MiniCssExtractPlugin.loader在生产环境替代style-loader,避免 CSS 内联导致 HTML 膨胀;postcss-preset-env的stage: 3表示启用 CSS Custom Properties(变量)、@media (prefers-reduced-motion)等已进入候选推荐的标准——比stage: 0(实验特性)更稳妥。

// webpack.config.js 片段:HTML 自动化 const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { plugins: [ new HtmlWebpackPlugin({ template: './public/index.html', // 以 public/index.html 为模板 filename: 'index.html', // 输出到 build/index.html inject: 'body', // script 标签注入到 body 底部 minify: { removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeStyleLinkTypeAttributes: true, keepClosingSlash: true, minifyJS: true, minifyCSS: true, minifyURLs: true, } }) ] };

HtmlWebpackPlugin是关键:它读取public/index.html(含<title><%= htmlWebpackPlugin.options.title %></title>这类 EJS 模板语法),注入main.[hash].js和vendor.[hash].js,再压缩 HTML。你只需维护public/index.html,不用手动改 script 标签——这点在客户临时要求加百度统计代码时救了命。

2.5 避坑:Webpack 构建与本地开发的 4 个高频翻车点

现象:npm run dev启动后页面空白,控制台报错GET http://localhost:8080/css/main.css net::ERR_ABORTED 404

原因:webpack.config.js中devServer.contentBase未指向public/目录,或publicPath配置错误导致 CSS 路径解析失败。
解决:确认devServer配置含contentBase: path.join(__dirname, 'public'),且output.publicPath为'/';检查public/index.html中是否误写了<link rel="stylesheet" href="css/main.css">(应为<link rel="stylesheet" href="/css/main.css">)。

现象:npm run build后build/目录下无css/文件夹,所有 CSS 被打包进main.js

原因:MiniCssExtractPlugin.loader仅在mode: 'production'下生效,开发环境仍用style-loader;若误在rules中对所有环境都使用MiniCssExtractPlugin.loader,开发时会报错。
解决:在rules中用process.env.NODE_ENV === 'production'动态切换 loader:

use: [ process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader', 'css-loader', 'postcss-loader' ]
现象:IE11 下轮播图无法自动播放,控制台报错Object doesn't support property or method 'forEach'

原因:src/components/Carousel.js中用了Array.prototype.forEach,但core-js的 polyfill 未在入口文件顶部引入。
解决:在src/js/main.js第一行添加:

import 'core-js/stable'; import 'regenerator-runtime/runtime';

并确保webpack.config.js的entry.vendor包含这两个包,避免 polyfill 被 tree-shaking 移除。

现象:修改src/config.json后,npm run dev页面未实时更新,需手动刷新

原因:Webpack 默认不监听 JSON 文件变更,html-webpack-plugin的template选项只监控 HTML 文件。
解决:在webpack.config.js中为HtmlWebpackPlugin添加templateParameters函数,强制读取 JSON:

new HtmlWebpackPlugin({ template: './public/index.html', templateParameters: () => ({ config: require('./src/config.json') }) })

并在public/index.html中用<%= config.companyName %>插入变量,Webpack Dev Server 会自动监听config.json变更。


3. 核心模块拆解:轮播图组件从 DOM 操作到可配置化封装

3.1 组件定位:为什么轮播图必须是独立 JS 模块而非内联脚本?

企业官网首页轮播图常面临三类需求:① 市场部要每周更换 3 张 Banner 图;② 法务部要求每张图右下角加版权水印;③ 客服部反馈用户抱怨自动播放太快。若轮播逻辑写在index.html的<script>标签里,每次改需求都要找开发,且易与其他 JS 冲突。本项目将其封装为src/components/Carousel.js,暴露initCarousel(options)方法,实现「配置驱动」:只需改config.json或页面内 JS 调用参数,即可控制行为。

3.2 HTML 结构:语义化与无障碍优先

<!-- src/pages/home/index.html --> <section class="carousel">// src/components/Carousel.js class Carousel { constructor(element, options = {}) { this.element = element; this.track = element.querySelector('.carousel__track'); this.slides = Array.from(element.querySelectorAll('.carousel__slide')); this.indicators = element.querySelectorAll('.carousel__indicator'); this.prevBtn = element.querySelector('.carousel__prev'); this.nextBtn = element.querySelector('.carousel__next'); // 合并默认配置与传入配置 this.config = { interval: 5000, // 自动播放间隔(ms) pauseOnHover: true, // 鼠标悬停暂停 keyboard: true, // 键盘左右键控制 ...options }; this.currentIndex = 0; this.timer = null; this.init(); } init() { this.bindEvents(); this.updateIndicators(); this.startAutoPlay(); } bindEvents() { // 按钮点击事件 this.prevBtn?.addEventListener('click', () => this.prev()); this.nextBtn?.addEventListener('click', () => this.next()); // 指示器点击 this.indicators.forEach((indicator, index) => { indicator.addEventListener('click', () => this.goTo(index)); }); // 键盘控制 if (this.config.keyboard) { document.addEventListener('keydown', (e) => { if (e.key === 'ArrowLeft') this.prev(); if (e.key === 'ArrowRight') this.next(); }); } // 悬停暂停 if (this.config.pauseOnHover) { this.element.addEventListener('mouseenter', () => this.stopAutoPlay()); this.element.addEventListener('mouseleave', () => this.startAutoPlay()); } } goTo(index) { if (index === this.currentIndex || index < 0 || index >= this.slides.length) return; this.slides[this.currentIndex].classList.remove('is-active'); this.slides[index].classList.add('is-active'); this.indicators[this.currentIndex].setAttribute('aria-selected', 'false'); this.indicators[index].setAttribute('aria-selected', 'true'); this.currentIndex = index; this.updateIndicators(); } next() { const nextIndex = (this.currentIndex + 1) % this.slides.length; this.goTo(nextIndex); } prev() { const prevIndex = (this.currentIndex - 1 + this.slides.length) % this.slides.length; this.goTo(prevIndex); } startAutoPlay() { if (this.config.interval <= 0) return; this.timer = setInterval(() => this.next(), this.config.interval); } stopAutoPlay() { clearInterval(this.timer); } updateIndicators() { // 同步指示器 active 状态 this.indicators.forEach((indicator, index) => { indicator.classList.toggle('is-active', index === this.currentIndex); indicator.setAttribute('aria-selected', index === this.currentIndex); }); } } // 暴露初始化方法 export function initCarousel(selector, options = {}) { const elements = typeof selector === 'string' ? document.querySelectorAll(selector) : [selector]; elements.forEach(el => { if (!el.carouselInstance) { el.carouselInstance = new Carousel(el, options); } }); }

逻辑说明:

  • constructor接收 DOM 元素和配置对象,this.config合并默认值,避免options.interval || 5000这类易错写法;
  • bindEvents中this.prevBtn?.addEventListener使用可选链操作符,防止元素不存在时报错;
  • goTo(index)中index < 0 || index >= this.slides.length边界检查,避免数组越界;
  • startAutoPlay中if (this.config.interval <= 0) return允许传入interval: 0彻底禁用自动播放;
  • el.carouselInstance缓存实例,防止重复初始化(多次调用initCarousel时安全)。

3.4 配置化调用:在页面中零代码接入

<!-- src/pages/home/index.html 底部 --> <script type="module"> import { initCarousel } from '../../components/Carousel.js'; // 方式1:全局配置(适用于单个轮播) initCarousel('[data-carousel-id="hero"]', { interval: 4000, pauseOnHover: false }); // 方式2:多实例配置(首页+产品页各一个) initCarousel('.carousel--products', { interval: 6000, keyboard: false }); </script>

参数说明:

  • selector:CSS 选择器,支持[data-carousel-id="hero"]或.carousel;
  • interval:数字,单位毫秒,设为0则禁用自动播放;
  • pauseOnHover:布尔值,true时鼠标悬停暂停,false时无视悬停;
  • keyboard:布尔值,true时启用左右方向键,false时禁用(避免与表单输入冲突)。

3.5 避坑:轮播图组件的 3 个隐形陷阱

现象:轮播图在 iOS Safari 上触摸滑动卡顿,动画不流畅

原因:iOS Safari 对transform动画有硬件加速限制,若.carousel__slide未声明will-change: transform,GPU 不会预分配资源。
解决:在src/css/components/_carousel.css中添加:

.carousel__slide { will-change: transform; transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); }

cubic-bezier(0.25, 0.46, 0.45, 0.94)是模仿 iOS 原生滚动的弹性曲线,比ease-in-out更自然。

现象:轮播图自动播放时,用户点击指示器后,下一张仍按原定时器触发,导致跳帧

原因:goTo(index)方法未重置this.timer,setInterval继续执行,next()被重复调用。
解决:在goTo(index)开头添加this.stopAutoPlay();,并在goTo结束后重新this.startAutoPlay();:

goTo(index) { if (index === this.currentIndex || index < 0 || index >= this.slides.length) return; this.stopAutoPlay(); // 关键:重置定时器 this.slides[this.currentIndex].classList.remove('is-active'); this.slides[index].classList.add('is-active'); this.indicators[this.currentIndex].setAttribute('aria-selected', 'false'); this.indicators[index].setAttribute('aria-selected', 'true'); this.currentIndex = index; this.updateIndicators(); this.startAutoPlay(); // 重新启动 }
现象:轮播图在 Chrome 115+ 中首次加载时,第一张图闪一下才显示

原因:Chrome 新版对loading="eager"的解析延迟,且carousel__slide默认opacity: 0,JS 初始化前 CSS 已生效。
解决:在src/css/components/_carousel.css中添加初始状态:

.carousel__slide { opacity: 0; transition: opacity 0.3s; } .carousel__slide.is-active { opacity: 1; } /* 关键:隐藏未激活 slide,但确保首屏立即显示 */ .carousel__slide:first-child { opacity: 1 !important; }

同时在Carousel构造函数末尾强制触发一次updateIndicators(),确保首屏状态同步。


4. 构建与部署:从本地开发到 Nginx/CDN 的全流程验证

4.1 本地开发环境搭建:5 分钟完成 npm install 与启动

# 步骤1:解压项目 ZIP 包,进入根目录 cd enterprise-website # 步骤2:安装依赖(注意:必须用 Node.js 16+,Node 18 最佳) npm install # 步骤3:启动开发服务器(自动打开浏览器) npm run dev # 步骤4:验证是否成功 # - 浏览器访问 http://localhost:8080 # - 检查控制台无红色报错 # - 修改 src/config.json 的 companyName,保存后页面应实时更新(热更新生效)

提示:若npm run dev报错Error: Cannot find module 'webpack',说明全局未安装 webpack-cli,请运行npm install -g webpack-cli;若报错Error: ENOSPC: System limit for number of file watchers reached(Linux/macOS),执行echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p。

4.2 构建产物分析:build/ 目录的 4 个核心文件

运行npm run build后,build/目录生成以下关键文件:

文件路径说明验证方法
build/index.html主页 HTML,已注入main.[hash].js和vendor.[hash].js用 VS Code 打开,搜索<script,确认路径含 hash 且为/js/开头
build/js/main.[hash].js业务 JS(含 Carousel、表单校验等),已压缩用浏览器打开http://localhost:8080/js/main.[hash].js,确认 200 状态码
build/css/main.[hash].css样式文件,含@font-face、媒体查询等检查文件大小,正常应在 15~30KB(未 gzip)
build/404.html自定义 404 页面,与public/404.html一致将浏览器地址改为http://localhost:8080/xxx,确认显示定制 404

注意:build/是纯静态文件,不要手动修改其中任何文件——所有源码修改必须在src/下进行,再通过npm run build重新生成。

4.3 部署到 Nginx:3 行配置搞定反向代理

Nginx 配置示例(/etc/nginx/sites-available/your-site):

server { listen 80; server_name example.com; # 根目录指向 build/,非 src/ root /var/www/enterprise-website/build; index index.html; # 关键:所有请求 fallback 到 index.html,支持前端路由(虽本项目无,但预留) location / { try_files $uri $uri/ /index.html; } # 静态资源缓存 1 年 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; } # 防止 .htaccess、.git 等敏感文件被访问 location ~ /\. { deny all; } }

部署步骤:

  1. 将build/目录整体复制到服务器/var/www/enterprise-website/build;
  2. 执行sudo nginx -t验证配置语法;
  3. 执行sudo systemctl reload nginx重载配置;
  4. 浏览器访问http://example.com,确认页面正常,F12 查看 Network 面板,所有资源状态码为 200。

4.4 部署到 GitHub Pages:免服务器托管方案

若无自有服务器,GitHub Pages 是最佳选择:

# 步骤1:在 GitHub 创建空仓库(如 enterprise-website) # 步骤2:本地项目根目录执行 git init git add . git commit -m "initial commit" git branch -M main git remote add origin https://github.com/username/enterprise-website.git git push -u origin main # 步骤3:进入 GitHub 仓库 Settings → Pages → Source 选 "Deploy from a branch" → Branch "main" → Folder "/ (root)" # 步骤4:等待 1-2 分钟,访问 https://username.github.io/enterprise-website/

提示:GitHub Pages 默认域名是https://username.github.io/repo-name/,若需绑定自定义域名,在仓库 Settings → Pages → Custom domain 输入example.com,并在 DNS 解析中添加CNAME记录指向username.github.io。

4.5 避坑:部署后页面白屏或资源 404 的 4 种排查路径

现象:部署后页面白屏,Network 面板显示main.[hash].js返回 404

排查路径:

  1. 检查build/目录下是否存在该 JS 文件(ls build/js/);
  2. 检查 Nginxroot配置是否指向build/(非build/js/);
  3. 检查webpack.config.js中output.publicPath是否为'/'(若为'./',Nginx 会尝试请求./js/main.js);
  4. 检查build/index.html中<script>标签路径是否为/js/main.[hash].js(非js/main.[hash].js)。
现象:图片全部显示为小图标,Network 显示404

排查路径:

  1. 检查src/pages/home/index.html中<img src="/assets/images/...">的路径——/assets/是开发时路径,构建后应为/images/;
  2. 确认webpack.config.js中file-loader或asset-module配置已将assets/images/复制到build/images/;
  3. 检查build/目录下images/文件夹是否存在,文件名是否含 hash(如banner-1.a1b2c3d4.jpg);
  4. 修改 HTML 中图片路径为<img src="/images/banner-1.a1b2c3d4.jpg">(或使用html-webpack-plugin的templateParameters注入)。
现象:部署到 GitHub Pages 后,点击导航栏链接跳转 404(如/about/)

原因:GitHub Pages 默认不支持 SPA 路由,/about/请求会查找build/about/index.html,但项目是单页应用,实际只有build/index.html。
解决:在public/目录下创建404.html,内容与index.html完全一致(已内置),GitHub Pages 会自动将所有 404 请求重定向到404.html,而404.html中的 JS 会解析 URL 并渲染对应页面——这是企业官网常用 trick。

现象:HTTPS 页面中,http://开头的图片链接被浏览器拦截(Mixed Content)

原因:src/config.json中socialLinks或contact.email误填了http://协议。
解决:全局搜索http://,将所有外部链接改为https://或协议相对链接//example.com;检查src/pages/contact/index.html中表单action属性,确保为/api/contact(后端接口需支持 HTTPS)。


5. 二次开发实战:如何在 30 分钟内新增「客户案例」页面并接入 CMS

5.1 页面结构设计:基于现有架构的最小侵入式扩展

企业官网常需动态展示客户 Logo 和简短评价,但又不想引入复杂 CMS。本方案利用src/config.json的嵌套结构 +src/pages/customers/目录,实现「伪 CMS」:市场部只需编辑 JSON,无需碰代码。

首先,在src/config.json中新增customers数组:

{ "companyName": "智创科技", "customers": [ { "id": "techcorp", "name": "TechCorp", "logo": "/images/logo-techcorp.svg", "quote": "智创的解决方案帮助我们缩短了 40% 的产品上线周期。", "industry": "互联网" }, { "id": "medilife", "name": "MediLife", "logo": "/images/logo-medilife.png", "quote": "数据安全服务让我们顺利通过了 HIPAA 合规审计。", "industry": "医疗健康" } ] }

提示:logo路径为/images/,表示构建后图片位于build/images/;若图片在src/assets/images/,Webpack 会自动复制并哈希重命名,但需在webpack.config.js中配置asset-module处理 SVG/PNG。

5.2 创建客户案例页面:复用现有组件与样式

新建src/pages/customers/index.html:

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>客户案例 - <%= htmlWebpackPlugin.options.config.companyName %></title> <meta name="description" content="智创科技服务的全球客户案例,涵盖互联网、医疗健康、金融等行业"> <!-- 复用公共 CSS --> <link rel="stylesheet" href="/css/main.css"> </head> <body> <header class="header"> <div class="container"> <a href="/" class="header__logo"><%= htmlWebpackPlugin.options.config.companyName %></a> <nav class="header__nav"> <a href="/" class="header__link">首页</a> <a href="/about/" class="header__link">关于我们</a> <a href="/customers/" class="header__link is-active">客户案例</a> <a href="/contact/" class="header__link">联系我们</a> </nav> </div> </header> <main class="main"> <div class="container"> <h1 class="page-title">客户案例</h1> <p class="page-subtitle">我们与 <p> <a href="https://download.csdn.net/download/goodlife2018/10720614" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进