当前位置:首页 > 百科

cshtml调用外部js方法

```html cshtml调用外部js方法 - 一个老项目的调用踩坑记录

cshtml调用外部js方法

上周改一个ASP.NET Core MVC的老项目,视图层全是外部.cshtml文件,里面混着Razor语法和jQuery。调用有个需求要在页面加载后调用一个外部utils.js里的外部formatDate函数,结果折腾了半小时——不是调用函数没定义,就是外部执行顺序不对。这事看起来简单,调用但.cshtml里调外部JS,外部有几个坑绕不开。调用

最直接的外部方式,在.cshtml文件底部加一个<script>标签,调用引用外部JS,外部然后写调用逻辑。调用比如:

<script src="~/js/utils.js"></script> <script> var d = new Date(); document.getElementById("dateSpan").innerText = formatDate(d); </script>

注意顺序:先加载utils.js,外部再写调用代码。调用反过来浏览器会报formatDate is not defined。我见过有人把<script>放在<head>里,然后DOM还没加载完就去操作元素,又或者外部JS用了defer,结果调用时函数还没挂到window上。所以稳妥点,把引用和调用都放在</body>之前。

但Razor视图有个麻烦:布局页_Layout.cshtml通常已经加载了jQuery或bootstrap的JS。如果你在子视图里又引一遍jQuery,可能重复加载导致插件冲突。这时候更推荐把外部JS的引用放到布局页的@RenderSection("Scripts", required: false)里。子视图这样写:

@section Scripts {  <script src="~/js/utils.js"></script> <script> $(function() {  var d = new Date(); $('#dateSpan').text(formatDate(d)); }); </script> }

这样能保证外部JS在jQuery之后加载,而且只在需要的时候加载。不过有个细节:@section Scripts里的内容会被渲染到布局页里@RenderSection("Scripts")的位置,通常是</body>前。如果你布局页里@RenderSection写在了<head>里,那外部JS还是会提前加载,DOM操作可能失败。检查一下布局页的@RenderSection位置,别偷懒。

另一个场景:外部JS里的函数依赖页面上的某个变量,比如var userId = '@Model.UserId';。你可以在.cshtml里先定义这个变量,再调用外部函数。但注意Razor会把@Model.UserId直接输出成字符串,如果userId是数字,记得加引号或者用parseInt。我遇到过有人写var userId = @Model.UserId;,结果Model里是空字符串,编译出的JS变成var userId = ;,直接语法错误。用JSON.Encode更安全:

var userId = @Html.Raw(Json.Encode(Model.UserId));

还有一点,外部JS如果是ES6模块(用了export),那在.cshtml里不能直接用<script src>加载,得加type="module"。但加了type="module"之后,函数不会挂到window上,你得在模块里export,然后在另一个模块里import。可.cshtml里的内联<script>默认不是模块,所以要么改成<script type="module">,要么把外部JS改成UMD格式。我一般直接改成普通函数挂到window上,省事。毕竟老项目里用模块化,纯属给自己找麻烦。

最后说个实际案例。有个页面用了laydate日期插件,外部JS里封装了initDatePicker函数。我在.cshtml里这样写:

<script src="~/js/dateHelper.js"></script> <script> document.addEventListener('DOMContentLoaded', function() {  initDatePicker('startDate'); initDatePicker('endDate'); }); </script>

结果initDatePicker里用了layui.use,而layui的模块加载是异步的,DOMContentLoaded触发时layui还没加载完。改成在layui.use的回调里调用initDatePicker才解决。所以外部JS内部如果有异步依赖,调用时机不能只看DOM,还得看那个异步操作完成没。

总结一下我的习惯:外部JS引用放在布局页的@RenderSection("Scripts")里,子视图用@section Scripts包裹调用代码;避免在<head>里引用;传递Razor变量用Json.Encode;如果外部JS有异步加载,把调用逻辑放到它的回调里。这些细节没人会写在官方文档里,但每个都能让你多调半小时。

```

分享到: