在 ASP.NET Core 項目中使用 npm 管理你的前端組件包

来源:https://www.cnblogs.com/danvic712/archive/2019/05/10/10841579.html
-Advertisement-
Play Games

一、前言 在項目的前端開發中,對於絕大多數的小伙伴來說,當然,也包括我,不可避免的需要在項目中使用到一些第三方的組件包。這時,團隊中的小伙伴是選擇直接去組件的官網上下載,還是圖省事直接在網上搜索,然後從一些來源不明的地方下載,我們就無法管控了。同時,我們添加的組件間可能存在各種依賴關係,如果我們沒有 ...


 一、前言

  在項目的前端開發中,對於絕大多數的小伙伴來說,當然,也包括我,不可避免的需要在項目中使用到一些第三方的組件包。這時,團隊中的小伙伴是選擇直接去組件的官網上下載,還是圖省事直接在網上搜索,然後從一些來源不明的地方下載,我們就無法管控了。同時,我們添加的組件間可能存在各種依賴關係,如果我們沒有正確下載引用的話,到最後可能還是無法正常使用。

  因此,如何從可信的源下載組件包,以及如何輕鬆的解決各個組件間的依賴關係就成了我們需要解決的問題,那麼,有沒有一種工具可以幫我們解決這一問題?你好,有的,npm 瞭解一下。

  代碼倉儲:https://github.com/Lanesra712/grapefruit-common

 二、Step by Step

  在 .NET Framework 的項目中,我們可以在項目中通過 Nuget 下載安裝前端的組件包。但是 Nuget 更多的是作為 .NET 後端項目中的包管理器,在這裡管理前端的組件包顯得有些不太合適。

  於是,在 .NET Core 的時代到來後,伴隨著前端的發展,微軟在創建的示例項目中開始推薦我們使用 bower 來管理我們項目中的前端組件包,然後,bower is dead。。。。

  所以這裡,我採用 npm 作為我們的 ASP.NET Core 項目中的前端包管理器。

  1、安裝 Node 環境

  Node.js 是一個能夠在服務端運行 Javascript 的執行環境,也就是說,Javascript 不僅可以用於前端,也可以構建後端服務了。而 npm 則是 Node.js 官方提供的包管理工具,所以在使用 npm 之前,需要在我們的電腦上安裝 Node.js 環境。

  當然,如果你之前有開發過 Vue、Angular 這類的前端項目,你肯定已經安裝好了。如果沒有,打開 Node.js 的官網(https://nodejs.org/en/download),根據你正在使用的操作系統信息,選擇安裝包下載就可以了。

  如果你使用的是 window 系統,很簡單,下載 msi 安裝包,一路 next 即可。在最新版本的 Node.js 安裝包中,npm 是隨著 Node.js 的安裝一起完成的。我們可以使用下麵的命令進行驗證,當可以列印出你安裝的版本信息,則說明安裝已經完成了。

//1、node.js 版本
node -v

//2、npm 版本
npm -v

  2、使用 npm 安裝包

  這篇文章的示例項目,我採用的是 ASP.NET Core 2.2 預設生成的 MVC 項目,因為在寫文章的過程中有過更換解決方案,所以文章中的截圖可能會出現名稱前後不對應的情況,還請見諒。

  當示例項目創建完成後,會自動在項目中引用 bootstrap 和 jquery,所以,我們就在這個項目的基礎上,嘗試採用 npm 來管理我們的前端組件包。

  右擊我們的項目,添加一個 package.json 配置文件。在這個 json 文件中定義了這個項目所需要的各種前端模塊,以及項目的配置信息(比如名稱、版本、許可證等等)。當我們從別處拷貝這個項目後,通過執行 npm install 命令,就會根據這個配置文件,自動下載項目中所需要引用的前端組件包。

  打開 package.json 文件,如果你選擇使用 VS 進行編輯的話,可以看到 VS 會自動幫我們出現代碼補齊提示。這裡我添加了一個 dependencies 節點,它與 devDependencies 節點都代表我們項目中需要安裝的插件。不同的是,devDependencies 裡面的插件只用於開發環境,不用於生產環境,而 dependencies 中引用的則是需要發佈到生產環境中的。

  例如,這裡我們需要在項目中添加 bootstrap 和 jquery,因為在正式發佈時如果缺少這兩個組件,就會導致我們的程式報錯,所以這裡我們需要添加到 dependencies 節點下,而像後面我們使用到的 gulp 的一系列插件,只有在我們進行項目開發時才會使用到,所以我們只需要添加到 devDependencies 即可。

  這裡我推薦使用命令行的方式添加組件,可以更好地展示出我們添加的組件需要添加哪些依賴。右鍵選中我們的示例項目,選擇 Open Command Line,打開控制台,輸入下列的命令,將 bootstrap 添加到我們的項目中。

  在 install 命令中我們添加了 --save 修飾,表示需要將 bootstrap 添加到 dependencies 節點下麵。如果,你需要將引用到的 package 安裝到 devDependencies 節點下,則需要使用 --save-dev 修飾。

npm install bootstrap --save

  可以看到,安裝完成後,npm 提示我們 bootstrap 依賴於 jquery 和 popper.js,所以這裡我們手動添加上這兩個依賴的組件。

  當我們安裝 jquery 的 1.9.1 版本後,因為之前的 jquery 版本存在一些安全隱患,所以 npm 會提示我們執行 npm audit 命令來查看當前項目中可能存在的安全隱患,以及對於如何解決這些隱患的建議。

  這裡我進行了版本升級,你可以根據自己的需求進行操作。請特別註意,當你在完成項目的基礎包載入後,後續對於包版本的升級一定要謹慎、謹慎、再謹慎。升級完成後的 package.json 文件如下所示。

{
  "version": "1.0.0",
  "name": "aspnetcore.npm.tutorial",
  "private": true,
  "devDependencies": {},
  "dependencies": {
    "bootstrap": "^4.3.1",
    "jquery": "^3.4.1",
    "popper.js": "^1.14.7"
  }
}

  在我們第一次執行 npm install 命令時,系統自動為我們創建了 package-lock.json 這個文件,用來記錄當前狀態下實際安裝的各個 npm package 的具體來源和版本號,當前項目下的 package-lock.json 文件如下。

{
  "name": "aspnetcore.npm.tutorial",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "bootstrap": {
      "version": "4.3.1",
      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz",
      "integrity": "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag=="
    },
    "jquery": {
      "version": "3.4.1",
      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz",
      "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw=="
    },
    "popper.js": {
      "version": "1.14.7",
      "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.7.tgz",
      "integrity": "sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ=="
    }
  }
}

  那麼 package-lock.json 這個文件到底有什麼用呢?

  因為我們在 npm 上下載的包遵循了大版本.次要版本.小版本的版本定義。例如,在上面的示例中,我們使用 npm install 命令安裝的 bootstrap 版本為 4.3.1,而在安裝插件包的時候,package.json 一般指定的是包的範圍,即只對插件包的大版本進行限定。因此,當別人拷貝了你的代碼,準備還原引用的包時,可能此時的 bootstrap 已經有 4.4.4 版本的了,這時,如果你使用了某些 4.3.1 版本中的特性,而在 4.4.4 版本中已經被移除的話,毫無疑問,你的代碼就會出 bug。

  而當項目中存在了 package-lock.json 文件之後,因為項目中引用的組件包版本和來源信息已經鎖定在了這個文件中了,此時,當別人拷貝了代碼,準備還原時,就可以準確的載入到你開發時使用的組件版本。當然,如果你修改了引用的包信息,當執行 npm install 命令時,package-lock.json 文件會同步更新。

  對於包的版本限定條件如下所示。

  指定版本:比如此例中 bootstrap 的版本為 4.3.1,當重新安裝時只安裝指定的 4.3.1 版本。

  波浪號(tilde) + 指定版本:比如 ~1.2.2,表示安裝1.2.x 的最新版本(不低於1.2.2),但是不安裝 1.3.x,也就是說安裝時不改變大版本號和次要版本號。

  插入號(caret) + 指定版本:比如 ˆ1.2.2,表示安裝1.x.x 的最新版本(不低於1.2.2),但是不安裝 2.x.x,也就是說安裝時不改變大版本號。需要註意的是,如果大版本號為0,則插入號的行為與波浪號相同。

  latest:始終安裝包的最新版本。

  3、gulp 配置

  當我們通過 npm 添加好需要使用的組件包後,就需要考慮如何在項目中使用。

  我們知道,在 ASP.NET Core 項目中,對於 web 項目中的靜態文件的獲取,通常是使用 StaticFileMiddleware 這個中間件。而 “{contentroot}/wwwroot” 這個目錄是對外發佈項目中的靜態文件預設使用的根目錄,也就是說,我們需要將使用到的 npm 包移動到 wwwroot 文件下。

  手動複製?em,工作量似乎有點大。

  不過,既然這裡我們使用到了 node.js,那麼這裡就可以使用 gulp.js 這個自動化任務執行器來幫我們實現這一功能,當然,你也可以根據自己的習慣使用別的工具。

  通過使用 gulp.js,我們就可以自動的執行移動文件,打包壓縮 js、css、image、刪除文件等等,幫我們省了再通過 bundle 去打包壓縮 css 和 js 文件的過程。

  在項目中使用 gulp.js 的前提,需要我們作為項目的開發依賴(devDependencies)安裝 gulp 和一些用到的 gulp 插件,因為會下載很多的東西,整個安裝的過程長短依據你的網路情況而定,嗯,請坐和放寬。

  在這個項目中使用到的 gulp 插件如下所示,如果你需要拷貝下麵的命令行的話,在執行時請刪除註釋內容。

//gulp.js
npm install gulp --save-dev

//壓縮 css
npm install gulp-clean-css --save-dev

//合併文件
npm install gulp-concat --save-dev

//壓縮 js
npm install gulp-uglify --save-dev

//重命名
npm install gulp-rename --save-dev

//刪除文件、文件夾
npm install rimraf --save-dev

//監聽文件變化
npm install gulp-changed --save-dev

  安裝完成後的 package.json 文件如下所示。

{
  "version": "1.0.0",
  "name": "aspnetcore.npm.tutorial",
  "private": true,
  "devDependencies": {
    "gulp": "^4.0.1",
    "gulp-changed": "^3.2.0",
    "gulp-clean-css": "^4.2.0",
    "gulp-concat": "^2.6.1",
    "gulp-rename": "^1.4.0",
    "gulp-uglify": "^3.0.2",
    "rimraf": "^2.6.3"
  },
  "dependencies": {
    "bootstrap": "^4.3.1",
    "jquery": "^3.4.1",
    "popper.js": "^1.14.7"
  }
}

  當我們安裝好所有的 gulp 組件包之後,在我們的項目根路徑下創建一個 gulpfile.js 文件,文件的內容如下所示。

/// <binding BeforeBuild='min' Clean='clean' ProjectOpened='auto' />
"use strict";

//載入使用到的 gulp 插件
const gulp = require("gulp"),
    rimraf = require("rimraf"),
    concat = require("gulp-concat"),
    cssmin = require("gulp-clean-css"),
    rename = require("gulp-rename"),
    uglify = require("gulp-uglify"),
    changed = require("gulp-changed");


//定義 wwwroot 下的各文件存放路徑
const paths = {
    root: "./wwwroot/",
    css: './wwwroot/css/',
    js: './wwwroot/js/',
    lib: './wwwroot/lib/'
};

//css
paths.cssDist = paths.css + "**/*.css";//匹配所有 css 的文件所在路徑
paths.minCssDist = paths.css + "**/*.min.css";//匹配所有 css 對應壓縮後的文件所在路徑
paths.concatCssDist = paths.css + "app.min.css";//將所有的 css 壓縮到一個 css 文件後的路徑

//js
paths.jsDist = paths.js + "**/*.js";//匹配所有 js 的文件所在路徑
paths.minJsDist = paths.js + "**/*.min.js";//匹配所有 js 對應壓縮後的文件所在路徑
paths.concatJsDist = paths.js + "app.min.js";//將所有的 js 壓縮到一個 js 文件後的路徑


//使用 npm 下載的前端組件包
const libs = [
    { name: "jquery", dist: "./node_modules/jquery/dist/**/*.*" },
    { name: "popper", dist: "./node_modules/popper.js/dist/**/*.*" },
    { name: "bootstrap", dist: "./node_modules/bootstrap/dist/**/*.*" },
];

//清除壓縮後的文件
gulp.task("clean:css", done => rimraf(paths.minCssDist, done));
gulp.task("clean:js", done => rimraf(paths.minJsDist, done));

gulp.task("clean", gulp.series(["clean:js", "clean:css"]));

//移動 npm 下載的前端組件包到 wwwroot 路徑下
gulp.task("move", done => {
    libs.forEach(function (item) {
        gulp.src(item.dist)
            .pipe(gulp.dest(paths.lib + item.name + "/dist"));
    });
    done()
});

//每一個 css 文件壓縮到對應的 min.css
gulp.task("min:css", () => {
    return gulp.src([paths.cssDist, "!" + paths.minCssDist], { base: "." })
        .pipe(rename({ suffix: '.min' }))
        .pipe(changed('.'))
        .pipe(cssmin())
        .pipe(gulp.dest('.'));
});

//將所有的 css 文件合併打包壓縮到 app.min.css 中
gulp.task("concatmin:css", () => {
    return gulp.src([paths.cssDist, "!" + paths.minCssDist], { base: "." })
        .pipe(concat(paths.concatCssDist))
        .pipe(changed('.'))
        .pipe(cssmin())
        .pipe(gulp.dest("."));
});

//每一個 js 文件壓縮到對應的 min.js
gulp.task("min:js", () => {
    return gulp.src([paths.jsDist, "!" + paths.minJsDist], { base: "." })
        .pipe(rename({ suffix: '.min' }))
        .pipe(changed('.'))
        .pipe(uglify())
        .pipe(gulp.dest('.'));
});

//將所有的 js 文件合併打包壓縮到 app.min.js 中
gulp.task("concatmin:js", () => {
    return gulp.src([paths.jsDist, "!" + paths.minJsDist], { base: "." })
        .pipe(concat(paths.concatJsDist))
        .pipe(changed('.'))
        .pipe(uglify())
        .pipe(gulp.dest("."));
});

gulp.task("min", gulp.series(["min:js", "min:css"]));
gulp.task("concatmin", gulp.series(["concatmin:js", "concatmin:css"]));


//監聽文件變化後自動執行
gulp.task("auto", () => {
    gulp.watch(paths.css, gulp.series(["min:css", "concatmin:css"]));
    gulp.watch(paths.js, gulp.series(["min:js", "concatmin:js"]));
});

  在 gulp.js 中主要有四個 API,就像我們項目中的 gulpfile 更多的是對於第三方插件的使用,而我們只需要通過 pipe 將任務中的每一步操作添加到任務隊列中即可。完整的 API 文檔,大家可以去官網去詳細查看 => https://gulpjs.com/docs/en/api/concepts

  gulp.src:根據匹配、或是路徑載入文件;

  gulp.dest:輸出文件到指定路徑;

  gulp.task:定義一個任務;

  gulp.watch:監聽文件變化。

  當我們創建好任務後,刪除 wwwroot 路徑下的引用的第三方組件包,運行我們的示例項目,毫無疑問,整個頁面的樣式都已經丟失了。

  選中 gulpfile.js,右鍵打開任務運行程式資源管理器。可以看到,系統會自動顯示出我們定義的所有任務,這時,我們可以滑鼠右鍵點擊任務,選中運行,即可執行我們的任務。

  然而,我們手動去執行似乎有些不智能,我們能不能自動執行某些任務呢?答案當然是可以,同樣是滑鼠右鍵點擊任務,點擊綁定菜單選項,我們就將定義好的任務綁定事件上。

  例如,在我的 gulpfile 中,我綁定了三個事件:生成解決方案前執行 min task,清理解決方案時執行 clean task,打開項目時執行 auto task,而 VS 也自動幫我們生成瞭如下的綁定腳本到我們的 gulpfile 上。

/// <binding BeforeBuild='min' Clean='clean' ProjectOpened='auto' />

  通過將綁定事件與 gulp API 進行結合,就可以很好的實現我們的需求。就像這裡,我在項目打開時綁定了自動監聽文件變化的任務,這時,只要我修改了 css、js 文件,gulp 就會自動幫我們實現對於文件的壓縮。

  PS:如果你將任務綁定到項目打開的事件上,則是需要下一次打開項目時才能自動執行。

 三、總結

    這一章主要是介紹瞭如何在我們的 ASP.NET Core 項目中通過 npm 管理我們的前端組件包,同時,使用 gulp 去執行一些移動文件、壓縮文件的任務。隨著這些年前端的發展,前端的開發越來越規範化,也越來越朝後端靠攏了,我們作為傳統意義上的後端程式猿,在涉及到前端的開發時,如果可以用到這些可以規範化我們的前端項目的特性,還是極好的。因為自己水平也很菜,很多東西並沒有很詳細的涉及到,可能還需要你在實際使用中進行進一步的探究,畢竟,實踐出真知。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 業務場景:有主表、子表兩個GridView點擊主表的行,會自動讀取主表對應的子表數據但是如果反覆點擊會導致反覆讀取,其實反覆點擊的時候只需要最後一次執行查詢,前面的幾次點擊都是無意義操作根據這一需求設計了一個函數:private static List Tup = new List();/// //... ...
  • 一、Excel理論知識 最新版NPOI2.4.1鏈接:https://pan.baidu.com/s/1iTgJi2hGsRQHyw2S_4dIUw 提取碼:adnq • 整個Excel表格叫做工作簿:WorkBook • 工作簿由以下幾部分組成 a.頁(Sheet); b.行(Row); c.單元 ...
  • 上一章快速陳述了自定義驗證功能添加的過程,我的第一個netcore2.2 api項目搭建(三) 但是並沒有真正的去實現,這一章將要實現驗證功能的添加。 這一章實現目標三:jwt認證授權添加 在netcore2.2中,只要添加很簡單的配置就能添加jwt功能了。至於jwt本身是啥大家自行去瞭解,這裡不做 ...
  • 用戶可以創建屬於自己的篩選方案用戶可以創建不同的篩選方案適用於不同場景需求●設計篩選條件●欄位排序方式●欄位是否顯示●欄位顯示順序●欄位顯示寬度菜單打開,如果有預設的篩選條件直接篩選不彈出篩選框修複第一次打開篩選彈窗條件的值沒有數據類型的問題直接拖拽列位置,調整列寬度可以自動保存到預設方案中系統預設... ...
  • 一 前言 Artech 分享了 "200行代碼,7個對象——讓你瞭解ASP.NET Core框架的本質" 。 用一個極簡的模擬框架闡述了ASP.NET Core框架最為核心的部分。 這裡一步步來完成這個迷你框架。 二 先來一段簡單的代碼 這段代碼非常簡單,啟動伺服器並監聽本地5000埠和處理請求。 ...
  • 序 學生時期,有過小組作業,當時分工一人做那麼兩三個頁面,然而在前端差不多的時候,我和另一個同學發生了爭執。當時用的是簡單的三層架構(DLL、BLL、UI),我個人覺得各寫各的吧,到時候合併,而他覺得應該把底層先寫好,他好直接調用中間層的方法。 到出來工作之後,接觸介面,想整理一下這個:介面到底是個 ...
  • 人所缺乏的不是才幹而是志向,不是成功的能力而是勤勞的意志。 哎!好久沒有寫博客了,今天就分享一些比較常用的對config文件的訪問一些方式。 首先 引用 using System.Configuration; 1、訪問config文件ConnectionStrings中的內容。 2、訪問config ...
  • 效果: 資料庫端: 前端展示: 實現原理: 1.在數據段建立兩個存儲過程 queryUserAnsawer(id) 根據用戶ID返回每一題的得分,主要是bcp exe時不能直接在sqlserver中執行sql查詢語句,只能先調用存儲過程 exportUserAnsawer(id,filepath,r ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...